Token

veSwapX (veSWPx)

Overview

Max Total Supply

0 veSWPx

Holders

0

Total Transfers

-

Market

Price

-

Onchain Market Cap

-

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information

Contract Source Code Verified (Exact Match)

Contract Name:
VotingEscrow

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 12 : VotingEscrow.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import {IERC721, IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import {IVeArtProxy} from "./interfaces/IVeArtProxy.sol";
import {IVotingEscrow} from "./interfaces/IVotingEscrow.sol";
import {Constants} from "./libraries/Constants.sol";

/// @title Voting Escrow
/// @notice veNFT implementation that escrows ERC-20 tokens in the form of an ERC-721 NFT
/// @notice Votes have a weight depending on time, so that users are committed to the future of (whatever they are voting for)
/// @author Modified from Solidly (https://github.com/solidlyexchange/solidly/blob/master/contracts/ve.sol)
/// @author Modified from Curve (https://github.com/curvefi/curve-dao-contracts/blob/master/contracts/VotingEscrow.vy)
/// @author Modified from Nouns DAO (https://github.com/withtally/my-nft-dao-project/blob/main/contracts/ERC721Checkpointable.sol)
/// @dev Vote weight decays linearly over time. Lock time cannot be more than `MAXTIME` (2 years).
contract VotingEscrow is Initializable, IERC721, IERC721Metadata, IVotes {
    enum DepositType {
        DEPOSIT_FOR_TYPE,
        CREATE_LOCK_TYPE,
        INCREASE_LOCK_AMOUNT,
        INCREASE_UNLOCK_TIME,
        MERGE_TYPE,
        SPLIT_TYPE
    }

    struct LockedBalance {
        int128 amount;
        uint start;
        uint end;
    }

    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 */

    /// @notice A checkpoint for marking delegated tokenIds from a given timestamp
    struct Checkpoint {
        uint timestamp;
        uint[] tokenIds;
    }

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    address public immutable token;
    address public voter;
    address public team;
    address public artProxy;

    address public distributor;

    mapping(uint => Point) public point_history; // epoch -> unsigned point

    /// @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;

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

    /// @notice Contract constructor
    /// @param token_addr `SWPx` token address
    constructor(address token_addr) {
        token = token_addr;
        _disableInitializers();
    }

    function initialize(address art_proxy, address _voter) external initializer {
        voter = _voter;
        team = msg.sender;
        artProxy = art_proxy;

        _entered_state = 1;

        point_history[0].blk = block.number;
        point_history[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);
    }

    /*//////////////////////////////////////////////////////////////
                                MODIFIERS
    //////////////////////////////////////////////////////////////*/

    /// @dev reentrancy guard
    uint8 internal constant _not_entered = 1;
    uint8 internal constant _entered = 2;
    uint8 internal _entered_state;
    modifier nonreentrant() {
        require(_entered_state != _entered);
        _entered_state = _entered;
        _;
        _entered_state = _not_entered;
    }

    /*///////////////////////////////////////////////////////////////
                             METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string constant public name = "veSwapX";
    string constant public symbol = "veSWPx";
    string constant public version = "1.0.0";
    uint8 constant public decimals = 18;

    function setTeam(address _team) external {
        require(msg.sender == team);
        team = _team;
    }

    function setArtProxy(address _proxy) external {
        require(msg.sender == team);
        artProxy = _proxy;
    }

    function setDistributor(address _distributor) external {
        require(msg.sender == team);
        distributor = _distributor;
    }

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

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

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

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

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

    /// @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 returns (uint) {
        return _balance(_owner);
    }

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

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

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

    mapping(uint => uint) public ownership_change;

    /// @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 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 returns (bool) {
        return (ownerToOperators[_owner])[_operator];
    }

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @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 {
        address owner = idToOwner[_tokenId];
        // Throws if `_tokenId` is not a valid NFT
        require(owner != address(0));
        // Throws if `_approved` is the current owner
        require(_approved != owner);
        // Check requirements
        bool senderIsOwner = (idToOwner[_tokenId] == msg.sender);
        bool senderIsApprovedForAll = (ownerToOperators[owner])[msg.sender];
        require(senderIsOwner || senderIsApprovedForAll);
        // 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 {
        // Throws if `_operator` is the `msg.sender`
        assert(_operator != msg.sender);
        ownerToOperators[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

    /* TRANSFER FUNCTIONS */
    /// @dev Clear an approval of a given address
    ///      Throws if `_owner` is not the current owner.
    function _clearApproval(address _owner, uint _tokenId) internal {
        // Throws if `_owner` is not the current owner
        assert(idToOwner[_tokenId] == _owner);
        if (idToApprovals[_tokenId] != address(0)) {
            // Reset approvals
            idToApprovals[_tokenId] = address(0);
        }
    }

    /// @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 returns (bool) {
        return _isApprovedOrOwner(_spender, _tokenId);
    }

    /// @dev Exeute 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");
        // Check requirements
        require(_isApprovedOrOwner(_sender, _tokenId));
        // Clear approval. Throws if `_from` is not the current owner
        _clearApproval(_from, _tokenId);
        // Remove NFT. Throws if `_tokenId` is not a valid NFT
        _removeTokenFrom(_from, _tokenId);
        // auto re-delegate
        _moveTokenDelegates(delegates(_from), delegates(_to), _tokenId);
        // Add NFT
        _addTokenTo(_to, _tokenId);
        // Set the block of ownership transfer (for Flash NFT protection)
        ownership_change[_tokenId] = block.number;
        // Log the transfer
        emit Transfer(_from, _to, _tokenId);
    }

    /// @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 {
        _transferFrom(_from, _to, _tokenId, msg.sender);
    }

    /// @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 {
        safeTransferFrom(_from, _to, _tokenId, "");
    }

    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 {
        _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 response) {
                if (response != IERC721Receiver(_to).onERC721Received.selector) {
                    revert("ERC721: ERC721Receiver rejected tokens");
                }
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert('ERC721: transfer to non ERC721Receiver implementer');
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

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

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @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  Get token by index
    function tokenOfOwnerByIndex(address _owner, uint _tokenIndex) external view returns (uint) {
        return ownerToNFTokenIdList[_owner][_tokenIndex];
    }

    /// @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 current_count = _balance(_to);

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

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

    /// @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
        assert(_to != address(0));
        // checkpoint for gov
        _moveTokenDelegates(address(0), delegates(_to), _tokenId);
        // Add NFT. Throws if `_tokenId` is owned by someone
        _addTokenTo(_to, _tokenId);
        emit Transfer(address(0), _to, _tokenId);
        return true;
    }

    /// @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 current_count = _balance(_from) - 1;
        uint current_index = tokenToOwnerIndex[_tokenId];

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

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

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

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

    function _burn(uint _tokenId) internal {
        require(_isApprovedOrOwner(msg.sender, _tokenId), "caller is not owner nor approved");

        address owner = ownerOf(_tokenId);

        // Clear approval
        approve(address(0), _tokenId);
        // checkpoint for gov
        _moveTokenDelegates(delegates(owner), address(0), _tokenId);
        // Remove token
        //_removeTokenFrom(msg.sender, _tokenId);
        _removeTokenFrom(owner, _tokenId);

        emit Transfer(owner, address(0), _tokenId);
    }

    /*//////////////////////////////////////////////////////////////
                             ESCROW STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint => uint) public user_point_epoch;
    mapping(uint => Point[1000000000]) public user_point_history; // user -> Point[user_epoch]
    mapping(uint => LockedBalance) public locked;
    uint public epoch;
    mapping(uint => int128) public slope_changes; // time -> signed slope change
    uint public supply;

    uint internal constant MAXTIME = 2 * 365 * 86400;
    int128 internal constant iMAXTIME = 2 * 365 * 86400;
    uint internal constant MULTIPLIER = 1 ether;

    event DepositCheckpoint(address user, uint256 tokenId, int128 bias, int128 slope, uint ts, uint blk);

    /*//////////////////////////////////////////////////////////////
                              ESCROW LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @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 get_last_user_slope(uint _tokenId) external view returns (int128) {
        uint uepoch = user_point_epoch[_tokenId];
        return user_point_history[_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 user_point_history__ts(uint _tokenId, uint _idx) external view returns (uint) {
        return user_point_history[_tokenId][_idx].ts;
    }

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

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

    /// @notice Record global and per-user data to checkpoint
    /// @param _tokenId NFT token ID. No user checkpoint if 0
    /// @param old_locked Pevious locked amount / end lock time for the user
    /// @param new_locked New locked amount / end lock time for the user
    function _checkpoint(
        uint _tokenId,
        LockedBalance memory old_locked,
        LockedBalance memory new_locked
    ) internal {
        Point memory u_old;
        Point memory u_new;
        int128 old_dslope = 0;
        int128 new_dslope = 0;
        uint _epoch = epoch;

        if (_tokenId != 0) {
            // Calculate slopes and biases
            // Kept at zero when they have to
            if (old_locked.end > block.timestamp && old_locked.amount > 0) {
                u_old.slope = old_locked.amount / iMAXTIME;
                u_old.bias = u_old.slope * int128(int256(old_locked.end - block.timestamp));
            }
            if (new_locked.end > block.timestamp && new_locked.amount > 0) {
                u_new.slope = new_locked.amount / iMAXTIME;
                u_new.bias = u_new.slope * int128(int256(new_locked.end - block.timestamp));
            }

            // Read values of scheduled changes in the slope
            // old_locked.end can be in the past and in the future
            // new_locked.end can ONLY by in the FUTURE unless everything expired: than zeros
            old_dslope = slope_changes[old_locked.end];
            if (new_locked.end != 0) {
                if (new_locked.end == old_locked.end) {
                    new_dslope = old_dslope;
                } else {
                    new_dslope = slope_changes[new_locked.end];
                }
            }
        }

        Point memory last_point = Point({bias: 0, slope: 0, ts: block.timestamp, blk: block.number});
        if (_epoch > 0) {
            last_point = point_history[_epoch];
        }
        uint last_checkpoint = last_point.ts;
        // initial_last_point 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 initial_last_point = last_point;
        uint block_slope = 0; // dblock/dt
        if (block.timestamp > last_point.ts) {
            block_slope = (MULTIPLIER * (block.number - last_point.blk)) / (block.timestamp - last_point.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 t_i = (last_checkpoint / Constants.EPOCH_LENGTH) * Constants.EPOCH_LENGTH;
            for (uint i = 0; i < 255; ++i) {
                // Hopefully it won't happen that this won't get used in 5 years!
                // If it does, users will be able to withdraw but vote weight will be broken
                t_i += Constants.EPOCH_LENGTH;
                int128 d_slope = 0;
                if (t_i > block.timestamp) {
                    t_i = block.timestamp;
                } else {
                    d_slope = slope_changes[t_i];
                }
                last_point.bias -= last_point.slope * int128(int256(t_i - last_checkpoint));
                last_point.slope += d_slope;
                if (last_point.bias < 0) {
                    // This can happen
                    last_point.bias = 0;
                }
                if (last_point.slope < 0) {
                    // This cannot happen - just in case
                    last_point.slope = 0;
                }
                last_checkpoint = t_i;
                last_point.ts = t_i;
                last_point.blk = initial_last_point.blk + (block_slope * (t_i - initial_last_point.ts)) / MULTIPLIER;
                _epoch += 1;
                if (t_i == block.timestamp) {
                    last_point.blk = block.number;
                    break;
                } else {
                    point_history[_epoch] = last_point;
                }
            }
        }

        epoch = _epoch;
        // Now point_history 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)
            last_point.slope += (u_new.slope - u_old.slope);
            last_point.bias += (u_new.bias - u_old.bias);
            if (last_point.slope < 0) {
                last_point.slope = 0;
            }
            if (last_point.bias < 0) {
                last_point.bias = 0;
            }
        }

        // Record the changed point into history
        point_history[_epoch] = last_point;

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

            if (new_locked.end > block.timestamp) {
                if (new_locked.end > old_locked.end) {
                    new_dslope -= u_new.slope; // old slope disappeared at this point
                    slope_changes[new_locked.end] = new_dslope;
                }
                // else: we recorded it already in old_dslope
            }
            // Now handle user history
            uint user_epoch = user_point_epoch[_tokenId] + 1;

            user_point_epoch[_tokenId] = user_epoch;
            u_new.ts = block.timestamp;
            u_new.blk = block.number;
            user_point_history[_tokenId][user_epoch] = u_new;

            emit DepositCheckpoint(ownerOf(_tokenId), _tokenId, u_new.bias, u_new.slope, u_new.ts, u_new.blk);
        }
    }

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

        supply = supply_before + _value;
        LockedBalance memory old_locked;
        (old_locked.amount, old_locked.start, old_locked.end) = (_locked.amount, _locked.start, _locked.end);
        // Adding to existing lock, or if a lock is expired - creating a new one
        _locked.amount += int128(int256(_value));
        if (unlock_time != 0) {
            _locked.end = unlock_time;
        }
        if (_locked.start == 0) {
            _locked.start = block.timestamp;
        }
        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, old_locked, _locked);

        address from = msg.sender;
        if (_value != 0 && deposit_type != DepositType.MERGE_TYPE && deposit_type != DepositType.SPLIT_TYPE ) {
            assert(IERC20(token).transferFrom(from, address(this), _value));
        }

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

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

    /// @notice Record global data to checkpoint
    function checkpoint() external {
        _checkpoint(0, LockedBalance(0, 0, 0), LockedBalance(0, 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 deposit_for(uint _tokenId, uint _value) external nonreentrant returns(uint votingPower) {
        LockedBalance memory _locked = locked[_tokenId];

        require(_value > 0); // dev: need non-zero value
        require(_locked.amount > 0, 'No existing lock found');
        require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');
        _deposit_for(_tokenId, _value, 0, _locked, DepositType.DEPOSIT_FOR_TYPE);

        return _balanceOfNFT(_tokenId, block.timestamp);
    }

    /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration`
    /// @param _value Amount to deposit
    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)
    /// @param _to Address to deposit
    function _create_lock(uint _value, uint _lock_duration, address _to) internal returns (uint) {
        uint unlock_time = (block.timestamp + _lock_duration) / Constants.EPOCH_LENGTH * Constants.EPOCH_LENGTH; // Locktime is rounded down to weeks

        require(_value > 0); // dev: need non-zero value
        require(unlock_time > block.timestamp, 'Can only lock until time in the future');
        require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 2 years max');

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

        _deposit_for(_tokenId, _value, unlock_time, locked[_tokenId], DepositType.CREATE_LOCK_TYPE);
        return _tokenId;
    }

    /// @notice Deposit `_value` tokens for `msg.sender` and lock for `_lock_duration`
    /// @param _value Amount to deposit
    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)
    function create_lock(uint _value, uint _lock_duration) external nonreentrant returns (uint newTokenId, uint votingPower) {
        newTokenId = _create_lock(_value, _lock_duration, msg.sender);
        votingPower = _balanceOfNFT(newTokenId, block.timestamp);
    }

    /// @notice Deposit `_value` tokens for `_to` and lock for `_lock_duration`
    /// @param _value Amount to deposit
    /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)
    /// @param _to Address to deposit
    function create_lock_for(uint _value, uint _lock_duration, address _to) external nonreentrant returns (uint newTokenId, uint votingPower) {
        newTokenId = _create_lock(_value, _lock_duration, _to);
        votingPower = _balanceOfNFT(newTokenId, block.timestamp);
    }

    /// @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 increase_amount(uint _tokenId, uint _value) external nonreentrant returns (uint votingPower) {
        assert(_isApprovedOrOwner(msg.sender, _tokenId));

        LockedBalance memory _locked = locked[_tokenId];

        assert(_value > 0); // dev: need non-zero value
        require(_locked.amount > 0, 'No existing lock found');
        require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');

        _deposit_for(_tokenId, _value, 0, _locked, DepositType.INCREASE_LOCK_AMOUNT);

        return _balanceOfNFT(_tokenId, block.timestamp);
    }

    /// @notice Extend the unlock time for `_tokenId`
    /// @param _lock_duration New number of seconds until tokens unlock
    function increase_unlock_time(uint _tokenId, uint _lock_duration) external nonreentrant returns (uint votingPower) {
        assert(_isApprovedOrOwner(msg.sender, _tokenId));

        LockedBalance memory _locked = locked[_tokenId];
        uint unlock_time = (block.timestamp + _lock_duration) / Constants.EPOCH_LENGTH * Constants.EPOCH_LENGTH; // Locktime is rounded down to weeks

        require(_locked.end > block.timestamp, 'Lock expired');
        require(_locked.amount > 0, 'Nothing is locked');
        require(unlock_time > _locked.end, 'Can only increase lock duration');
        require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 2 years max');

        _deposit_for(_tokenId, 0, unlock_time, _locked, DepositType.INCREASE_UNLOCK_TIME);

        return _balanceOfNFT(_tokenId, block.timestamp);
    }

    /// @notice Withdraw all tokens for `_tokenId`
    /// @dev Only possible if the lock has expired
    function withdraw(uint _tokenId) external nonreentrant {
        assert(_isApprovedOrOwner(msg.sender, _tokenId));
        require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");

        LockedBalance memory _locked = locked[_tokenId];
        require(block.timestamp >= _locked.end, "The lock didn't expire");

        uint value = uint(int256(_locked.amount));

        locked[_tokenId] = LockedBalance(0, 0, 0);
        uint supply_before = supply;
        supply = supply_before - 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, 0));

        assert(IERC20(token).transfer(msg.sender, value));

        // Burn the NFT
        _burn(_tokenId);

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

    /*///////////////////////////////////////////////////////////////
                           GAUGE VOTING STORAGE
    //////////////////////////////////////////////////////////////*/

    // 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 max_epoch Don't go beyond this epoch
    /// @return Approximate timestamp for block
    function _find_block_epoch(uint _block, uint max_epoch) internal view returns (uint) {
        // Binary search
        uint _min = 0;
        uint _max = max_epoch;
        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 (point_history[_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 = user_point_epoch[_tokenId];
        if (_epoch == 0) {
            return 0;
        } else {
            Point memory last_point = user_point_history[_tokenId][_epoch];
            last_point.bias -= last_point.slope * int128(int256(_t) - int256(last_point.ts));
            if (last_point.bias < 0) {
                last_point.bias = 0;
            }
            return uint(int256(last_point.bias));
        }
    }

    function balanceOfNFT(uint _tokenId) external view returns (uint) {
        if (ownership_change[_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
        assert(_block <= block.number);

        // Binary search
        uint _min = 0;
        uint _max = user_point_epoch[_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 (user_point_history[_tokenId][_mid].blk <= _block) {
                _min = _mid;
            } else {
                _max = _mid - 1;
            }
        }

        Point memory upoint = user_point_history[_tokenId][_min];

        uint max_epoch = epoch;
        uint _epoch = _find_block_epoch(_block, max_epoch);
        Point memory point_0 = point_history[_epoch];
        uint d_block = 0;
        uint d_t = 0;
        if (_epoch < max_epoch) {
            Point memory point_1 = point_history[_epoch + 1];
            d_block = point_1.blk - point_0.blk;
            d_t = point_1.ts - point_0.ts;
        } else {
            d_block = block.number - point_0.blk;
            d_t = block.timestamp - point_0.ts;
        }
        uint block_time = point_0.ts;
        if (d_block != 0) {
            block_time += (d_t * (_block - point_0.blk)) / d_block;
        }

        upoint.bias -= upoint.slope * int128(int256(block_time - upoint.ts));
        if (upoint.bias >= 0) {
            return uint(uint128(upoint.bias));
        } else {
            return 0;
        }
    }

    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 _block Block to calculate the total voting power at
    /// @return Total voting power at `_block`
    function totalSupplyAt(uint _block) external view returns (uint) {
        assert(_block <= block.number);
        uint _epoch = epoch;
        uint target_epoch = _find_block_epoch(_block, _epoch);

        Point memory point = point_history[target_epoch];
        uint dt = 0;
        if (target_epoch < _epoch) {
            Point memory point_next = point_history[target_epoch + 1];
            if (point.blk != point_next.blk) {
                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 _supply_at(point, point.ts + dt);
    }
    /// @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 _supply_at(Point memory point, uint t) internal view returns (uint) {
        Point memory last_point = point;
        uint t_i = (last_point.ts / Constants.EPOCH_LENGTH) * Constants.EPOCH_LENGTH;
        for (uint i = 0; i < 255; ++i) {
            t_i += Constants.EPOCH_LENGTH;
            int128 d_slope = 0;
            if (t_i > t) {
                t_i = t;
            } else {
                d_slope = slope_changes[t_i];
            }
            last_point.bias -= last_point.slope * int128(int256(t_i - last_point.ts));
            if (t_i == t) {
                break;
            }
            last_point.slope += d_slope;
            last_point.ts = t_i;
        }

        if (last_point.bias < 0) {
            last_point.bias = 0;
        }
        return uint(uint128(last_point.bias));
    }

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

    /// @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 last_point = point_history[_epoch];
        return _supply_at(last_point, t);
    }

    /*///////////////////////////////////////////////////////////////
                            GAUGE VOTING LOGIC
    //////////////////////////////////////////////////////////////*/

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

    function setVoter(address _voter) external {
        require(msg.sender == team);
        voter = _voter;
    }

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

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

    function attach(uint _tokenId) external {
        require(msg.sender == voter);
        attachments[_tokenId] = attachments[_tokenId] + 1;
    }

    function detach(uint _tokenId) external {
        require(msg.sender == voter);
        attachments[_tokenId] = attachments[_tokenId] - 1;
    }

    function merge(uint _from, uint _to) external returns (uint votingPower) {
        require(attachments[_from] == 0 && !voted[_from], "attached");
        require(_from != _to);
        require(_isApprovedOrOwner(msg.sender, _from));
        require(_isApprovedOrOwner(msg.sender, _to));

        LockedBalance memory _locked0 = locked[_from];
        LockedBalance memory _locked1 = locked[_to];
        require(_locked0.end > block.timestamp && _locked1.end > block.timestamp);

        uint value0 = uint(int256(_locked0.amount));
        uint end = _locked0.end >= _locked1.end ? _locked0.end : _locked1.end;

        _locked1.start = _locked0.start >= _locked1.start ? _locked1.start : _locked0.start;

        locked[_from] = LockedBalance(0, 0, 0);

        supply = supply - value0;

        _checkpoint(_from, _locked0, LockedBalance(0, 0, 0));
        _burn(_from);
        _deposit_for(_to, value0, end, _locked1, DepositType.MERGE_TYPE);

        return _balanceOfNFT(_to, block.timestamp);
    }


    /**
     * @notice split NFT into multiple
     * @param amounts   % of split
     * @param _tokenId  NFTs ID
     */
    function split(uint[] memory amounts, uint _tokenId) external returns(uint256[] memory newTokenIds, uint256[] memory votingPowers) {

        // check permission and vote
        require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");
        require(_isApprovedOrOwner(msg.sender, _tokenId));


        // save old data and totalWeight
        address _to = idToOwner[_tokenId];
        LockedBalance memory _locked = locked[_tokenId];
        require(_locked.amount > 0); // dev: need non-zero value

        // require()

        // reset supply, _deposit_for increase it
        supply = supply - uint128(_locked.amount);

        uint i;
        uint totalWeight = 0;
        for(i = 0; i < amounts.length; i++){
            totalWeight += amounts[i];
        }

        // remove old data
        locked[_tokenId] = LockedBalance(0, 0, 0);
        _checkpoint(_tokenId, _locked, LockedBalance(0, 0, 0));
        _burn(_tokenId);

        // save end
        uint unlock_time = _locked.end;
        require(unlock_time > block.timestamp, 'Can only lock until time in the future');
        require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 2 years max');

        newTokenIds = new uint256[](amounts.length);
        votingPowers = new uint256[](amounts.length);

        // mint
        uint _value = 0;
        for(i = 0; i < amounts.length; i++){
            ++tokenId;
            _tokenId = tokenId;
            _mint(_to, _tokenId);
            _value = uint128(_locked.amount) * amounts[i] / totalWeight;
            locked[_tokenId].start = _locked.start;
            _deposit_for(_tokenId, _value, unlock_time, locked[_tokenId], DepositType.SPLIT_TYPE);

            newTokenIds[i] = _tokenId;
            votingPowers[i] = _balanceOfNFT(_tokenId, block.timestamp);
        }
    }

    /*///////////////////////////////////////////////////////////////
                            DAO VOTING STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice The EIP-712 typehash for the contract's domain
    bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");

    /// @notice The EIP-712 typehash for the delegation struct used by the contract
    bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");

    /// @notice A record of each accounts delegate
    mapping(address => address) private _delegates;
    uint public constant MAX_DELEGATES = 1024; // avoid too much gas

    /// @notice A record of delegated token checkpoints for each account, by index
    mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;

    /// @notice The number of checkpoints for each account
    mapping(address => uint32) public numCheckpoints;

    /// @notice A record of states for signing / validating signatures
    mapping(address => uint) public nonces;

    /**
     * @notice Overrides the standard `Comp.sol` delegates mapping to return
     * the delegator's own address if they haven't delegated.
     * This avoids having to delegate to oneself.
     */
    function delegates(address delegator) public view returns (address) {
        address current = _delegates[delegator];
        return current == address(0) ? delegator : current;
    }

    /**
     * @notice Gets the current votes balance for `account`
     * @param account The address to get votes balance
     * @return The number of current votes for `account`
     */
    function getVotes(address account) external view returns (uint) {
        uint32 nCheckpoints = numCheckpoints[account];
        if (nCheckpoints == 0) {
            return 0;
        }
        uint[] storage _tokenIds = checkpoints[account][nCheckpoints - 1].tokenIds;
        uint votes = 0;
        for (uint i = 0; i < _tokenIds.length; i++) {
            uint tId = _tokenIds[i];
            votes = votes + _balanceOfNFT(tId, block.timestamp);
        }
        return votes;
    }

    function getPastVotesIndex(address account, uint timestamp) public view returns (uint32) {
        uint32 nCheckpoints = numCheckpoints[account];
        if (nCheckpoints == 0) {
            return 0;
        }
        // First check most recent balance
        if (checkpoints[account][nCheckpoints - 1].timestamp <= timestamp) {
            return (nCheckpoints - 1);
        }

        // Next check implicit zero balance
        if (checkpoints[account][0].timestamp > timestamp) {
            return 0;
        }

        uint32 lower = 0;
        uint32 upper = nCheckpoints - 1;
        while (upper > lower) {
            uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            Checkpoint storage cp = checkpoints[account][center];
            if (cp.timestamp == timestamp) {
                return center;
            } else if (cp.timestamp < timestamp) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return lower;
    }

    function getPastVotes(address account, uint timestamp)
        public
        view
        returns (uint)
    {
        uint32 _checkIndex = getPastVotesIndex(account, timestamp);
        // Sum votes
        uint[] storage _tokenIds = checkpoints[account][_checkIndex].tokenIds;
        uint votes = 0;
        for (uint i = 0; i < _tokenIds.length; i++) {
            uint tId = _tokenIds[i];
            // Use the provided input timestamp here to get the right decay
            votes = votes + _balanceOfNFT(tId, timestamp);
        }
        return votes;
    }

    function getPastTotalSupply(uint256 timestamp) external view returns (uint) {
        return totalSupplyAtT(timestamp);
    }

    /*///////////////////////////////////////////////////////////////
                             DAO VOTING LOGIC
    //////////////////////////////////////////////////////////////*/

    function _moveTokenDelegates(
        address srcRep,
        address dstRep,
        uint _tokenId
    ) internal {
        if (srcRep != dstRep && _tokenId > 0) {
            if (srcRep != address(0)) {
                uint32 srcRepNum = numCheckpoints[srcRep];
                uint[] storage srcRepOld = srcRepNum > 0
                    ? checkpoints[srcRep][srcRepNum - 1].tokenIds
                    : checkpoints[srcRep][0].tokenIds;
                uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep);
                uint[] storage srcRepNew = checkpoints[srcRep][
                    nextSrcRepNum
                ].tokenIds;
                // All the same except _tokenId
                for (uint i = 0; i < srcRepOld.length; i++) {
                    uint tId = srcRepOld[i];
                    if (tId != _tokenId) {
                        srcRepNew.push(tId);
                    }
                }

                numCheckpoints[srcRep] = srcRepNum + 1;
            }

            if (dstRep != address(0)) {
                uint32 dstRepNum = numCheckpoints[dstRep];
                uint[] storage dstRepOld = dstRepNum > 0
                    ? checkpoints[dstRep][dstRepNum - 1].tokenIds
                    : checkpoints[dstRep][0].tokenIds;
                uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep);
                uint[] storage dstRepNew = checkpoints[dstRep][
                    nextDstRepNum
                ].tokenIds;
                // All the same plus _tokenId
                require(
                    dstRepOld.length + 1 <= MAX_DELEGATES,
                    "dstRep would have too many tokenIds"
                );
                for (uint i = 0; i < dstRepOld.length; i++) {
                    uint tId = dstRepOld[i];
                    dstRepNew.push(tId);
                }
                dstRepNew.push(_tokenId);

                numCheckpoints[dstRep] = dstRepNum + 1;
            }
        }
    }

    function _findWhatCheckpointToWrite(address account)
        internal
        view
        returns (uint32)
    {
        uint _timestamp = block.timestamp;
        uint32 _nCheckPoints = numCheckpoints[account];

        if (
            _nCheckPoints > 0 &&
            checkpoints[account][_nCheckPoints - 1].timestamp == _timestamp
        ) {
            return _nCheckPoints - 1;
        } else {
            return _nCheckPoints;
        }
    }

    function _moveAllDelegates(
        address owner,
        address srcRep,
        address dstRep
    ) internal {
        // You can only redelegate what you own
        if (srcRep != dstRep) {
            if (srcRep != address(0)) {
                uint32 srcRepNum = numCheckpoints[srcRep];
                uint[] storage srcRepOld = srcRepNum > 0
                    ? checkpoints[srcRep][srcRepNum - 1].tokenIds
                    : checkpoints[srcRep][0].tokenIds;
                uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep);
                uint[] storage srcRepNew = checkpoints[srcRep][
                    nextSrcRepNum
                ].tokenIds;
                // All the same except what owner owns
                for (uint i = 0; i < srcRepOld.length; i++) {
                    uint tId = srcRepOld[i];
                    if (idToOwner[tId] != owner) {
                        srcRepNew.push(tId);
                    }
                }

                numCheckpoints[srcRep] = srcRepNum + 1;
            }

            if (dstRep != address(0)) {
                uint32 dstRepNum = numCheckpoints[dstRep];
                uint[] storage dstRepOld = dstRepNum > 0
                    ? checkpoints[dstRep][dstRepNum - 1].tokenIds
                    : checkpoints[dstRep][0].tokenIds;
                uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep);
                uint[] storage dstRepNew = checkpoints[dstRep][
                    nextDstRepNum
                ].tokenIds;
                uint ownerTokenCount = ownerToNFTokenCount[owner];
                require(
                    dstRepOld.length + ownerTokenCount <= MAX_DELEGATES,
                    "dstRep would have too many tokenIds"
                );
                // All the same
                for (uint i = 0; i < dstRepOld.length; i++) {
                    uint tId = dstRepOld[i];
                    dstRepNew.push(tId);
                }
                // Plus all that's owned
                for (uint i = 0; i < ownerTokenCount; i++) {
                    uint tId = ownerToNFTokenIdList[owner][i];
                    dstRepNew.push(tId);
                }

                numCheckpoints[dstRep] = dstRepNum + 1;
            }
        }
    }

    function _delegate(address delegator, address delegatee) internal {
        /// @notice differs from `_delegate()` in `Comp.sol` to use `delegates` override method to simulate auto-delegation
        address currentDelegate = delegates(delegator);

        _delegates[delegator] = delegatee;

        emit DelegateChanged(delegator, currentDelegate, delegatee);
        _moveAllDelegates(delegator, currentDelegate, delegatee);
    }

    /**
     * @notice Delegate votes from `msg.sender` to `delegatee`
     * @param delegatee The address to delegate votes to
     */
    function delegate(address delegatee) public {
        if (delegatee == address(0)) delegatee = msg.sender;
        return _delegate(msg.sender, delegatee);
    }

    function delegateBySig(
        address delegatee,
        uint nonce,
        uint expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public {
        require(delegatee != msg.sender);
        require(delegatee != address(0));

        bytes32 domainSeparator = keccak256(
            abi.encode(
                DOMAIN_TYPEHASH,
                keccak256(bytes(name)),
                keccak256(bytes(version)),
                block.chainid,
                address(this)
            )
        );
        bytes32 structHash = keccak256(
            abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)
        );
        bytes32 digest = keccak256(
            abi.encodePacked("\x19\x01", domainSeparator, structHash)
        );
        address signatory = ecrecover(digest, v, r, s);
        require(
            signatory != address(0),
            "VotingEscrow::delegateBySig: invalid signature"
        );
        require(
            nonce == nonces[signatory]++,
            "VotingEscrow::delegateBySig: invalid nonce"
        );
        require(
            block.timestamp <= expiry,
            "VotingEscrow::delegateBySig: signature expired"
        );
        return _delegate(signatory, delegatee);
    }
}

File 2 of 12 : IVotes.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;

/**
 * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
 *
 * _Available since v4.5._
 */
interface IVotes {
    /**
     * @dev Emitted when an account changes their delegate.
     */
    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);

    /**
     * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
     */
    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);

    /**
     * @dev Returns the current amount of votes that `account` has.
     */
    function getVotes(address account) external view returns (uint256);

    /**
     * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
     */
    function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);

    /**
     * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
     *
     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
     * Votes that have not been delegated are still part of total supply, even though they would not participate in a
     * vote.
     */
    function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);

    /**
     * @dev Returns the delegate that `account` has chosen.
     */
    function delegates(address account) external view returns (address);

    /**
     * @dev Delegates votes from the sender to `delegatee`.
     */
    function delegate(address delegatee) external;

    /**
     * @dev Delegates votes from signer to `delegatee`.
     */
    function delegateBySig(
        address delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}

File 3 of 12 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/Address.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

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

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Internal function that returns the initialized version. Returns `_initialized`
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Internal function that returns the initialized version. Returns `_initializing`
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

File 4 of 12 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

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(uint256 tokenId) external view returns (string memory);
}

File 5 of 12 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/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`.
     *
     * 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;

    /**
     * @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 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: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * 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 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 the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @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);
}

File 6 of 12 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @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 `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

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

pragma solidity ^0.8.1;

/**
 * @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;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 8 of 12 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @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 9 of 12 : IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function transfer(address recipient, uint amount) external returns (bool);
    function decimals() external view returns (uint8);
    function symbol() external view returns (string memory);
    function balanceOf(address) external view returns (uint);
    function transferFrom(address sender, address recipient, uint amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint);
    function approve(address spender, uint value) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
}

File 10 of 12 : IVeArtProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IVeArtProxy {
    function _tokenURI(uint _tokenId, uint _balanceOf, uint _locked_end, uint _value) external pure returns (string memory output);
}

File 11 of 12 : IVotingEscrow.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IVotingEscrow {

    struct Point {
        int128 bias;
        int128 slope; // # -dweight / dt
        uint256 ts;
        uint256 blk; // block
    }

    struct LockedBalance {
        int128 amount;
        uint start;
        uint end;
    }

    function create_lock_for(uint _value, uint _lock_duration, address _to) external returns (uint);

    function locked(uint id) external view returns(LockedBalance memory);
    function tokenOfOwnerByIndex(address _owner, uint _tokenIndex) external view returns (uint);

    function token() external view returns (address);
    function team() external returns (address);
    function epoch() external view returns (uint);
    function point_history(uint loc) external view returns (Point memory);
    function user_point_history(uint tokenId, uint loc) external view returns (Point memory);
    function user_point_epoch(uint tokenId) external view returns (uint);

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

    function voted(uint) external view returns (bool);
    function attachments(uint) external view returns (uint);
    function voting(uint tokenId) external;
    function abstain(uint tokenId) external;
    function attach(uint tokenId) external;
    function detach(uint tokenId) external;

    function checkpoint() external;
    function deposit_for(uint tokenId, uint value) external;

    function balanceOfAtNFT(uint _tokenId, uint _block) external view returns (uint);
    function balanceOfNFT(uint _id) external view returns (uint);
    function balanceOf(address _owner) external view returns (uint);
    function totalSupply() external view returns (uint);
    function supply() external view returns (uint);


    function decimals() external view returns(uint8);
}

File 12 of 12 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

library Constants {
    uint256 internal constant EPOCH_LENGTH = 7 days;
}

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_addr","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":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","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 VotingEscrow.DepositType","name":"deposit_type","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"int128","name":"bias","type":"int128"},{"indexed":false,"internalType":"int128","name":"slope","type":"int128"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blk","type":"uint256"}],"name":"DepositCheckpoint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","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":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DELEGATES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"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":[],"name":"artProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"attach","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":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"checkpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"create_lock","outputs":[{"internalType":"uint256","name":"newTokenId","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"create_lock_for","outputs":[{"internalType":"uint256","name":"newTokenId","type":"uint256"},{"internalType":"uint256","name":"votingPower","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"deposit_for","outputs":[{"internalType":"uint256","name":"votingPower","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"detach","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":"timestamp","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPastVotesIndex","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"get_last_user_slope","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increase_amount","outputs":[{"internalType":"uint256","name":"votingPower","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"increase_unlock_time","outputs":[{"internalType":"uint256","name":"votingPower","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"art_proxy","type":"address"},{"internalType":"address","name":"_voter","type":"address"}],"name":"initialize","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":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"locked__end","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"locked__start","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":[{"internalType":"uint256","name":"votingPower","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"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":"ownership_change","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"point_history","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"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":"address","name":"_proxy","type":"address"}],"name":"setArtProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_distributor","type":"address"}],"name":"setDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_team","type":"address"}],"name":"setTeam","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_voter","type":"address"}],"name":"setVoter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slope_changes","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"split","outputs":[{"internalType":"uint256[]","name":"newTokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"votingPowers","type":"uint256[]"}],"stateMutability":"nonpayable","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":"team","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"user_point_epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"user_point_history","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"user_point_history__ts","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":[],"name":"voter","outputs":[{"internalType":"address","name":"","type":"address"}],"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"}]

60a06040523480156200001157600080fd5b506040516200540a3803806200540a833981016040819052620000349162000114565b6001600160a01b0381166080526200004b62000052565b5062000146565b600054610100900460ff1615620000bf5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff908116101562000112576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6000602082840312156200012757600080fd5b81516001600160a01b03811681146200013f57600080fd5b9392505050565b60805161529a6200017060003960008181610c2401528181611144015261376d015261529a6000f3fe608060405234801561001057600080fd5b506004361061043e5760003560e01c80637116c60c11610236578063bfe109281161013b578063e441135c116100c3578063f1127ed811610087578063f1127ed814610bbe578063f8a0576314610be9578063fbd3a29d14610c0c578063fc0c546a14610c1f578063fd4a77f114610c4657600080fd5b8063e441135c14610b15578063e7a324dc14610b35578063e7e242d414610b5c578063e985e9c514610b6f578063ee99fe2814610bab57600080fd5b8063c87b56dd1161010a578063c87b56dd14610a8b578063d1c2babb14610a9e578063d1febfb914610ab1578063d4e54c3b14610aef578063e0514aba14610b0257600080fd5b8063bfe1092814610a4a578063c1f0fb9f14610a5d578063c2c4c5c114610a70578063c3cda52014610a7857600080fd5b806395d89b41116101be578063a183af521161018d578063a183af52146109aa578063a22cb465146109bd578063a4d855df146109d0578063b45a3c0e146109e3578063b88d4fde14610a3757600080fd5b806395d89b411461094c578063981b24d014610971578063986b7d8a146109845780639ab24eb01461099757600080fd5b806385f2aef21161020557806385f2aef2146108e75780638c2c9baf146108fa5780638e539e8c1461090d5780638fbb38ff14610920578063900cf0cf1461094357600080fd5b80637116c60c1461087e578063711974841461089157806375619ab5146108b45780637ecebe00146108c757600080fd5b806335cf668a116103475780635594a045116102cf5780636352211e116102935780636352211e146107d457806365fc3873146107fd5780636f548837146108255780636fcfff451461084557806370a082311461086b57600080fd5b80635594a0451461077157806356afe74414610784578063587cde1e146107a55780635c19a95c146107b85780635f5b0c32146107cb57600080fd5b8063461f711c11610316578063461f711c146106e857806346c96aac1461070e578063485cc955146107275780634bc2a6571461073a57806354fd4d501461074d57600080fd5b806335cf668a1461068c5780633a46b1a8146106af57806342842e0e146106c2578063430c2081146106d557600080fd5b806318160ddd116103ca57806325a58b561161039957806325a58b56146106105780632e1a7d4d146106165780632e720f7d146106295780632f745c591461063c578063313ce5671461067257600080fd5b806318160ddd146105bb5780631c984bc3146105c357806320606b70146105d657806323b872dd146105fd57600080fd5b8063081812fc11610411578063081812fc146104f7578063095cf5c614610538578063095ea7b31461054d5780630d6a2033146105605780631376f3da1461058057600080fd5b806301ffc9a714610443578063047fc9aa1461048557806306fdde031461049c5780630758c7d8146104cf575b600080fd5b610470610451366004614833565b6001600160e01b03191660009081526005602052604090205460ff1690565b60405190151581526020015b60405180910390f35b61048e60145481565b60405190815260200161047c565b6104c2604051806040016040528060078152602001660eccaa6eec2e0b60cb1b81525081565b60405161047c91906148a8565b6104e26104dd3660046148d7565b610c59565b60405163ffffffff909116815260200161047c565b610520610505366004614901565b6000908152600a60205260409020546001600160a01b031690565b6040516001600160a01b03909116815260200161047c565b61054b61054636600461491a565b610dcc565b005b61054b61055b3660046148d7565b610e05565b61048e61056e366004614901565b60156020526000908152604090205481565b61059361058e366004614935565b610eed565b60408051600f95860b81529390940b602084015292820152606081019190915260800161047c565b61048e610f34565b61048e6105d1366004614935565b610f44565b61048e7f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61054b61060b366004614957565b610f76565b4361048e565b61054b610624366004614901565b610f87565b61054b61063736600461491a565b611267565b61048e61064a3660046148d7565b6001600160a01b03919091166000908152600d60209081526040808320938352929052205490565b61067a601281565b60405160ff909116815260200161047c565b61048e61069a366004614901565b60009081526011602052604090206001015490565b61048e6106bd3660046148d7565b6112a0565b61054b6106d0366004614957565b61133f565b6104706106e33660046148d7565b61135a565b6106fb6106f6366004614901565b61136d565b604051600f9190910b815260200161047c565b600054610520906201000090046001600160a01b031681565b61054b610735366004614993565b6113b0565b61054b61074836600461491a565b611624565b6104c2604051806040016040528060058152602001640312e302e360dc1b81525081565b600254610520906001600160a01b031681565b610797610792366004614a0d565b611665565b60405161047c929190614af4565b6105206107b336600461491a565b6119fd565b61054b6107c636600461491a565b611a2d565b61048e61040081565b6105206107e2366004614901565b6000908152600860205260409020546001600160a01b031690565b61081061080b366004614935565b611a4b565b6040805192835260208301919091520161047c565b61048e610833366004614901565b600c6020526000908152604090205481565b6104e261085336600461491a565b60196020526000908152604090205463ffffffff1681565b61048e61087936600461491a565b611a9f565b61048e61088c366004614901565b611abd565b6106fb61089f366004614901565b601360205260009081526040902054600f0b81565b61054b6108c236600461491a565b611b1d565b61048e6108d536600461491a565b601a6020526000908152604090205481565b600154610520906001600160a01b031681565b61048e610908366004614935565b611b56565b61048e61091b366004614901565b611b62565b61047061092e366004614901565b60166020526000908152604090205460ff1681565b61048e60125481565b6104c2604051806040016040528060068152602001650eccaa6aea0f60d31b81525081565b61048e61097f366004614901565b611b6d565b61054b610992366004614901565b611d0f565b61048e6109a536600461491a565b611d59565b61048e6109b8366004614935565b611e2c565b61054b6109cb366004614b27565b611f4c565b61048e6109de366004614935565b611fd0565b610a176109f1366004614901565b601160205260009081526040902080546001820154600290920154600f9190910b919083565b60408051600f9490940b845260208401929092529082015260600161047c565b61054b610a45366004614b86565b6121a0565b600354610520906001600160a01b031681565b61054b610a6b366004614901565b61233f565b61054b612374565b61054b610a86366004614c31565b6123c2565b6104c2610a99366004614901565b61272b565b61048e610aac366004614935565b61286f565b610593610abf366004614901565b600460205260009081526040902080546001820154600290920154600f82810b93600160801b909304900b919084565b610810610afd366004614c91565b612a7c565b61048e610b10366004614935565b612ad1565b61048e610b23366004614901565b600f6020526000908152604090205481565b61048e7fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b61048e610b6a366004614901565b612add565b610470610b7d366004614993565b6001600160a01b039182166000908152600b6020908152604080832093909416825291909152205460ff1690565b61048e610bb9366004614935565b612b05565b61048e610bcc366004614cc6565b601860209081526000928352604080842090915290825290205481565b61048e610bf7366004614901565b60009081526011602052604090206002015490565b61054b610c1a366004614901565b612bec565b6105207f000000000000000000000000000000000000000000000000000000000000000081565b61054b610c54366004614901565b612c23565b6001600160a01b03821660009081526019602052604081205463ffffffff16808203610c89576000915050610dc6565b6001600160a01b03841660009081526018602052604081208491610cae600185614d11565b63ffffffff16815260208101919091526040016000205411610cdd57610cd5600182614d11565b915050610dc6565b6001600160a01b0384166000908152601860209081526040808320838052909152902054831015610d12576000915050610dc6565b600080610d20600184614d11565b90505b8163ffffffff168163ffffffff161115610dc15760006002610d458484614d11565b610d4f9190614d4c565b610d599083614d11565b6001600160a01b038816600090815260186020908152604080832063ffffffff851684529091529020805491925090879003610d9b57509350610dc692505050565b8054871115610dac57819350610dba565b610db7600183614d11565b92505b5050610d23565b509150505b92915050565b6001546001600160a01b03163314610de357600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000818152600860205260409020546001600160a01b031680610e2757600080fd5b806001600160a01b0316836001600160a01b031603610e4557600080fd5b6000828152600860209081526040808320546001600160a01b038581168552600b845282852033808752945291909320549216149060ff168180610e865750805b610e8f57600080fd5b6000848152600a602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918716917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050505050565b601060205281600052604060002081633b9aca008110610f0c57600080fd5b6003020180546001820154600290920154600f82810b9550600160801b90920490910b925084565b6000610f3f42611abd565b905090565b600082815260106020526040812082633b9aca008110610f6657610f66614d6f565b6003020160010154905092915050565b610f8283838333612c5b565b505050565b60075460ff1660011901610f9a57600080fd5b6007805460ff19166002179055610fb13382612d2a565b610fbd57610fbd614d85565b600081815260156020526040902054158015610fe8575060008181526016602052604090205460ff16155b61100d5760405162461bcd60e51b815260040161100490614d9b565b60405180910390fd5b60008181526011602090815260409182902082516060810184528154600f0b8152600182015492810192909252600201549181018290529042101561108d5760405162461bcd60e51b8152602060048201526016602482015275546865206c6f636b206469646e27742065787069726560501b6044820152606401611004565b80516040805160608101825260008082526020808301828152838501838152888452601190925293909120915182546001600160801b0319166001600160801b03909116178255915160018201559051600290910155601454600f9190910b906110f78282614dbd565b601481905550611128848460405180606001604052806000600f0b8152602001600081526020016000815250612d8d565b60405163a9059cbb60e01b8152336004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015611195573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111b99190614dd4565b6111c5576111c5614d85565b6111ce8461343d565b60408051858152602081018490524281830152905133917f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca94919081900360600190a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c8161123c8482614dbd565b6040805192835260208301919091520160405180910390a150506007805460ff191660011790555050565b6001546001600160a01b0316331461127e57600080fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6000806112ad8484610c59565b6001600160a01b038516600090815260186020908152604080832063ffffffff851684529091528120919250600190910190805b82548110156113355760008382815481106112fe576112fe614d6f565b9060005260206000200154905061131581886134fe565b61131f9084614df1565b925050808061132d90614e09565b9150506112e1565b5095945050505050565b610f82838383604051806020016040528060008152506121a0565b60006113668383612d2a565b9392505050565b6000818152600f60209081526040808320546010909252822081633b9aca00811061139a5761139a614d6f565b6003020154600160801b9004600f0b9392505050565b600054610100900460ff16158080156113d05750600054600160ff909116105b806113ea5750303b1580156113ea575060005460ff166001145b61144d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401611004565b6000805460ff191660011790558015611470576000805461ff0019166101001790555b600080546001600160a01b03808516620100000262010000600160b01b0319909216919091178255600180546001600160a01b0319908116331782556002805493881693909116929092179091556007805460ff199081168317909155437f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ee55427f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ed5560056020527fc01909ce2b517f8cd3a46ae0cfde9179f9b675cf633d3d84c8226585cc73c15680548216831790557f072ad3113145b5af48d301e3b9fc3bd1c97c3f26a14f5d44904b71469875631e8054821683179055635b5e139f60e01b83527f3b767bd59d7164fff7ec5b80ca1165d9d6e12ee8656896fac4159b0760bfd9f78054909116909117905560065460405190913091600080516020615245833981519152908290a46006546040516000903090600080516020615245833981519152908390a48015610f82576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b6001546001600160a01b0316331461163b57600080fd5b600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b6000818152601560205260409020546060908190158015611695575060008381526016602052604090205460ff16155b6116b15760405162461bcd60e51b815260040161100490614d9b565b6116bb3384612d2a565b6116c457600080fd5b6000838152600860209081526040808320546011835281842082516060810184528154600f0b808252600183015495820195909552600290910154928101929092526001600160a01b03169290911361171c57600080fd5b8051601454611734916001600160801b031690614dbd565b6014556000805b875182101561177d5787828151811061175657611756614d6f565b6020026020010151816117699190614df1565b90508161177581614e09565b92505061173b565b6040805160608082018352600080835260208084018281528486018381528d845260118352868420955186546001600160801b0319166001600160801b03909116178655905160018601555160029094019390935583519182018452808252918101829052918201526117f39088908590612d8d565b6117fc8761343d565b60408301514281116118205760405162461bcd60e51b815260040161100490614e22565b61182e6303c2670042614df1565b81111561184d5760405162461bcd60e51b815260040161100490614e68565b885167ffffffffffffffff811115611867576118676149c6565b604051908082528060200260200182016040528015611890578160200160208202803683370190505b509650885167ffffffffffffffff8111156118ad576118ad6149c6565b6040519080825280602002602001820160405280156118d6578160200160208202803683370190505b50955060008093505b89518410156119f0576006600081546118f790614e09565b90915550600654985061190a868a6135d2565b50828a858151811061191e5761191e614d6f565b602002602001015186600001516001600160801b031661193e9190614e9f565b6119489190614ebe565b60208087015160008c8152601183526040908190206001810183905581516060810183528154600f0b81529384019290925260029091015490820152909150611998908a90839085906005613631565b888885815181106119ab576119ab614d6f565b6020026020010181815250506119c189426134fe565b8785815181106119d3576119d3614d6f565b6020908102919091010152836119e881614e09565b9450506118df565b5050505050509250929050565b6001600160a01b038082166000908152601760205260408120549091168015611a265780611366565b5090919050565b6001600160a01b038116611a3e5750335b611a483382613881565b50565b600754600090819060ff1660011901611a6357600080fd5b6007805460ff19166002179055611a7b8484336138f4565b9150611a8782426134fe565b6007805460ff19166001179055919491935090915050565b6001600160a01b038116600090815260096020526040812054610dc6565b601254600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b93810193909352600181015491830191909152600201546060820152909190611b1581856139e9565b949350505050565b6001546001600160a01b03163314611b3457600080fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b60006113668383613aea565b6000610dc682611abd565b600043821115611b7f57611b7f614d85565b6012546000611b8e8483613dc3565b600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925083831015611c9d576000600481611bf1866001614df1565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606080830182905285015191925014611c975782606001518160600151611c5d9190614dbd565b83604001518260400151611c719190614dbd565b6060850151611c80908a614dbd565b611c8a9190614e9f565b611c949190614ebe565b91505b50611cec565b43826060015114611cec576060820151611cb79043614dbd565b6040830151611cc69042614dbd565b6060840151611cd59089614dbd565b611cdf9190614e9f565b611ce99190614ebe565b90505b611d0582828460400151611d009190614df1565b6139e9565b9695505050505050565b6000546201000090046001600160a01b03163314611d2c57600080fd5b600081815260156020526040902054611d4790600190614dbd565b60009182526015602052604090912055565b6001600160a01b03811660009081526019602052604081205463ffffffff16808203611d885750600092915050565b6001600160a01b038316600090815260186020526040812081611dac600185614d11565b63ffffffff1663ffffffff16815260200190815260200160002060010190506000805b8254811015611e23576000838281548110611dec57611dec614d6f565b90600052602060002001549050611e0381426134fe565b611e0d9084614df1565b9250508080611e1b90614e09565b915050611dcf565b50949350505050565b60075460009060ff1660011901611e4257600080fd5b6007805460ff19166002179055611e593384612d2a565b611e6557611e65614d85565b60008381526011602090815260409182902082516060810184528154600f0b8152600182015492810192909252600201549181019190915282611eaa57611eaa614d85565b60008160000151600f0b13611efa5760405162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b6044820152606401611004565b42816040015111611f1d5760405162461bcd60e51b815260040161100490614ed2565b611f2c84846000846002613631565b611f3684426134fe565b9150506007805460ff1916600117905592915050565b336001600160a01b03831603611f6457611f64614d85565b336000818152600b602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b60075460009060ff1660011901611fe657600080fd5b6007805460ff19166002179055611ffd3384612d2a565b61200957612009614d85565b600083815260116020908152604080832081516060810183528154600f0b815260018201549381019390935260020154908201529062093a808061204d8642614df1565b6120579190614ebe565b6120619190614e9f565b9050428260400151116120a55760405162461bcd60e51b815260206004820152600c60248201526b131bd8dac8195e1c1a5c995960a21b6044820152606401611004565b60008260000151600f0b136120f05760405162461bcd60e51b8152602060048201526011602482015270139bdd1a1a5b99c81a5cc81b1bd8dad959607a1b6044820152606401611004565b816040015181116121435760405162461bcd60e51b815260206004820152601f60248201527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006044820152606401611004565b6121516303c2670042614df1565b8111156121705760405162461bcd60e51b815260040161100490614e68565b61217f85600083856003613631565b61218985426134fe565b925050506007805460ff1916600117905592915050565b6121ac84848433612c5b565b823b1561233957604051630a85bd0160e11b81526001600160a01b0384169063150b7a02906121e5903390889087908790600401614f16565b6020604051808303816000875af1925050508015612220575060408051601f3d908101601f1916820190925261221d91810190614f49565b60015b6122c8573d80801561224e576040519150601f19603f3d011682016040523d82523d6000602084013e612253565b606091505b5080516000036122c05760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401611004565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b146123375760405162461bcd60e51b815260206004820152602660248201527f4552433732313a2045524337323152656365697665722072656a656374656420604482015265746f6b656e7360d01b6064820152608401611004565b505b50505050565b6000546201000090046001600160a01b0316331461235c57600080fd5b6000908152601660205260409020805460ff19169055565b6123c0600060405180606001604052806000600f0b815260200160008152602001600081525060405180606001604052806000600f0b8152602001600081526020016000815250612d8d565b565b336001600160a01b038716036123d757600080fd5b6001600160a01b0386166123ea57600080fd5b60408051808201825260078152660eccaa6eec2e0b60cb1b6020918201528151808301835260058152640312e302e360dc1b9082015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f1de6a1c189dcc4ee6a95fb1fdc3a550060accb8f042ce71a27c9dd46bee30732818401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608201524660808201523060a0808301919091528351808303909101815260c0820184528051908301207fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60e08301526001600160a01b038a1661010083015261012082018990526101408083018990528451808403909101815261016083019094528351939092019290922061190160f01b61018084015261018283018290526101a2830181905290916000906101c20160408051601f198184030181528282528051602091820120600080855291840180845281905260ff8a169284019290925260608301889052608083018790529092509060019060a0016020604051602081039080840390855afa1580156125ab573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166126255760405162461bcd60e51b815260206004820152602e60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a20696e7660448201526d616c6964207369676e617475726560901b6064820152608401611004565b6001600160a01b0381166000908152601a6020526040812080549161264983614e09565b9190505589146126ae5760405162461bcd60e51b815260206004820152602a60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a20696e76604482015269616c6964206e6f6e636560b01b6064820152608401611004565b874211156127155760405162461bcd60e51b815260206004820152602e60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a2073696760448201526d1b985d1d5c9948195e1c1a5c995960921b6064820152608401611004565b61271f818b613881565b50505050505050505050565b6000818152600860205260409020546060906001600160a01b03166127925760405162461bcd60e51b815260206004820152601b60248201527f517565727920666f72206e6f6e6578697374656e7420746f6b656e00000000006044820152606401611004565b60008281526011602090815260409182902082516060810184528154600f0b81526001820154928101929092526002908101549282019290925290546001600160a01b031663dd9ec149846127e781426134fe565b604080860151865191516001600160e01b031960e087901b16815261282a949392600f0b9060040193845260208401929092526040830152606082015260800190565b600060405180830381865afa158015612847573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113669190810190614f66565b60008281526015602052604081205415801561289a575060008381526016602052604090205460ff16155b6128b65760405162461bcd60e51b815260040161100490614d9b565b8183036128c257600080fd5b6128cc3384612d2a565b6128d557600080fd5b6128df3383612d2a565b6128e857600080fd5b60008381526011602081815260408084208151606080820184528254600f90810b8352600180850154848801526002948501548487019081528b8a529787529785902085519283018652805490910b825296870154948101949094529401549082015290514210801561295e5750428160400151115b61296757600080fd5b60008260000151600f0b9050600082604001518460400151101561298f578260400151612995565b83604001515b90508260200151846020015110156129b15783602001516129b7565b82602001515b6020848101919091526040805160608101825260008082528184018181528284018281528c83526011909552929020905181546001600160801b0319166001600160801b03909116178155905160018201559051600290910155601454612a1f908390614dbd565b601481905550612a50878560405180606001604052806000600f0b8152602001600081526020016000815250612d8d565b612a598761343d565b612a67868383866004613631565b612a7186426134fe565b979650505050505050565b600754600090819060ff1660011901612a9457600080fd5b6007805460ff19166002179055612aac8585856138f4565b9150612ab882426134fe565b6007805460ff1916600117905591959194509092505050565b600061136683836134fe565b6000818152600c6020526040812054439003612afb57506000919050565b610dc682426134fe565b60075460009060ff1660011901612b1b57600080fd5b6007805460ff1916600290811790915560008481526011602090815260409182902082516060810184528154600f0b8152600182015492810192909252909201549082015282612b6a57600080fd5b60008160000151600f0b13612bba5760405162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b6044820152606401611004565b42816040015111612bdd5760405162461bcd60e51b815260040161100490614ed2565b611f2c84846000846000613631565b6000546201000090046001600160a01b03163314612c0957600080fd5b600081815260156020526040902054611d47906001614df1565b6000546201000090046001600160a01b03163314612c4057600080fd5b6000908152601660205260409020805460ff19166001179055565b600082815260156020526040902054158015612c86575060008281526016602052604090205460ff16155b612ca25760405162461bcd60e51b815260040161100490614d9b565b612cac8183612d2a565b612cb557600080fd5b612cbf8483613e49565b612cc98483613eb0565b612ce4612cd5856119fd565b612cde856119fd565b84613f31565b612cee8383614293565b6000828152600c60205260408082204390555183916001600160a01b03808716929088169160008051602061524583398151915291a450505050565b600081815260086020908152604080832054600a8352818420546001600160a01b03918216808652600b855283862088841680885295529285205492938085149392909116149060ff168280612d7d5750815b80612a7157509695505050505050565b60408051608081018252600080825260208201819052918101829052606081019190915260408051608081018252600080825260208201819052918101829052606081019190915260125460009081908715612ef157428760400151118015612dfd575060008760000151600f0b135b15612e40578651612e13906303c2670090614fd4565b600f0b60208601526040870151612e2b904290614dbd565b8560200151612e3a9190615012565b600f0b85525b428660400151118015612e5a575060008660000151600f0b135b15612e9d578551612e70906303c2670090614fd4565b600f0b60208501526040860151612e88904290614dbd565b8460200151612e979190615012565b600f0b84525b604080880151600090815260136020528190205490870151600f9190910b935015612ef1578660400151866040015103612ed957829150612ef1565b604080870151600090815260136020522054600f0b91505b604080516080810182526000808252602082015242918101919091524360608201528115612f66575060008181526004602090815260409182902082516080810184528154600f81810b8352600160801b909104900b9281019290925260018101549282019290925260029091015460608201525b604081015181600042831015612fb3576040840151612f859042614dbd565b6060850151612f949043614dbd565b612fa690670de0b6b3a7640000614e9f565b612fb09190614ebe565b90505b600062093a80612fc38186614ebe565b612fcd9190614e9f565b905060005b60ff81101561314757612fe862093a8083614df1565b9150600042831115612ffc57429250613010565b50600082815260136020526040902054600f0b5b61301a8684614dbd565b87602001516130299190615012565b875188906130389083906150a7565b600f0b9052506020870180518291906130529083906150f7565b600f90810b90915288516000910b1215905061306d57600087525b60008760200151600f0b121561308557600060208801525b60408088018490528501519295508592670de0b6b3a7640000906130a99085614dbd565b6130b39086614e9f565b6130bd9190614ebe565b85606001516130cc9190614df1565b60608801526130dc600189614df1565b97504283036130f15750436060870152613147565b6000888152600460209081526040918290208951918a01516001600160801b03908116600160801b02921691909117815590880151600182015560608801516002909101555061314081614e09565b9050612fd2565b505060128590558b156131d2578860200151886020015161316891906150a7565b8460200181815161317991906150f7565b600f0b9052508851885161318d91906150a7565b8451859061319c9083906150f7565b600f90810b90915260208601516000910b121590506131bd57600060208501525b60008460000151600f0b12156131d257600084525b6000858152600460209081526040918290208651918701516001600160801b03908116600160801b02921691909117815590850151600182015560608501516002909101558b1561342f57428b60400151111561328657602089015161323890886150f7565b96508a604001518a604001510361325b57602088015161325890886150a7565b96505b60408b810151600090815260136020522080546001600160801b0319166001600160801b0389161790555b428a6040015111156132de578a604001518a6040015111156132de5760208801516132b190876150a7565b60408b810151600090815260136020522080546001600160801b0319166001600160801b03831617905595505b60008c8152600f60205260408120546132f8906001614df1565b905080600f60008f815260200190815260200160002081905550428960400181815250504389606001818152505088601060008f815260200190815260200160002082633b9aca00811061334e5761334e614d6f565b825160208401516001600160801b03908116600160801b0291161760039190910291909101908155604082015160018201556060909101516002909101557f8d83e324303a2f547eb8572fb6b42fb04620c4a95f09c9a1c8c6e5ac8f4624ed6133cc8e6000908152600860205260409020546001600160a01b031690565b8e8b600001518c602001518d604001518e60600151604051613425969594939291906001600160a01b039690961686526020860194909452600f92830b6040860152910b6060840152608083015260a082015260c00190565b60405180910390a1505b505050505050505050505050565b6134473382612d2a565b6134935760405162461bcd60e51b815260206004820181905260248201527f63616c6c6572206973206e6f74206f776e6572206e6f7220617070726f7665646044820152606401611004565b6000818152600860205260408120546001600160a01b0316906134b69083610e05565b6134ca6134c2826119fd565b600084613f31565b6134d48183613eb0565b60405182906000906001600160a01b03841690600080516020615245833981519152908390a45050565b6000828152600f602052604081205480820361351e576000915050610dc6565b600084815260106020526040812082633b9aca00811061354057613540614d6f565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b60208301526001810154928201839052600201546060820152915061358c9085615146565b816020015161359b9190615012565b815182906135aa9083906150a7565b600f90810b90915282516000910b121590506135c557600081525b51600f0b9150610dc69050565b60006001600160a01b0383166135ea576135ea614d85565b6135f86000612cde856119fd565b6136028383614293565b60405182906001600160a01b03851690600090600080516020615245833981519152908290a450600192915050565b60145482906136408682614df1565b601455604080516060810182526000808252602080830182815283850192835286519187015194870151909252929052600f9190910b81528251879084906136899083906150f7565b600f0b905250851561369d57604083018690525b82602001516000036136b0574260208401525b600088815260116020908152604091829020855181546001600160801b0319166001600160801b03909116178155908501516001820155908401516002909101556136fc888285612d8d565b33871580159061371e5750600485600581111561371b5761371b615185565b14155b801561373c5750600585600581111561373957613739615185565b14155b156137e6576040516323b872dd60e01b81526001600160a01b038281166004830152306024830152604482018a90527f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303816000875af11580156137b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137da9190614dd4565b6137e6576137e6614d85565b8360400151816001600160a01b03167fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6248b8b894260405161382a949392919061519b565b60405180910390a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c8361385e8a82614df1565b6040805192835260208301919091520160405180910390a1505050505050505050565b600061388c836119fd565b6001600160a01b0384811660008181526017602052604080822080546001600160a01b031916888616908117909155905194955093928516927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4610f82838284614329565b60008062093a80806139068642614df1565b6139109190614ebe565b61391a9190614e9f565b90506000851161392957600080fd5b4281116139485760405162461bcd60e51b815260040161100490614e22565b6139566303c2670042614df1565b8111156139755760405162461bcd60e51b815260040161100490614e68565b60066000815461398490614e09565b9091555060065461399584826135d2565b5060008181526011602090815260409182902082516060810184528154600f0b815260018083015493820193909352600290910154928101929092526139e091839189918691613631565b95945050505050565b600080839050600062093a80808360400151613a059190614ebe565b613a0f9190614e9f565b905060005b60ff811015613ac257613a2a62093a8083614df1565b9150600085831115613a3e57859250613a52565b50600082815260136020526040902054600f0b5b6040840151613a619084614dbd565b8460200151613a709190615012565b84518590613a7f9083906150a7565b600f0b905250858303613a925750613ac2565b8084602001818151613aa491906150f7565b600f0b9052505060408301829052613abb81614e09565b9050613a14565b5060008260000151600f0b1215613ad857600082525b50516001600160801b03169392505050565b600043821115613afc57613afc614d85565b6000838152600f6020526040812054815b6080811015613b9c5781831015613b9c5760006002613b2c8486614df1565b613b37906001614df1565b613b419190614ebe565b6000888152601060205260409020909150869082633b9aca008110613b6857613b68614d6f565b600302016002015411613b7d57809350613b8b565b613b88600182614dbd565b92505b50613b9581614e09565b9050613b0d565b50600085815260106020526040812083633b9aca008110613bbf57613bbf614d6f565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b602083015260018101549282019290925260029091015460608201526012549091506000613c148783613dc3565b600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608201529192508084841015613cf3576000600481613c78876001614df1565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b93810193909352600181015491830191909152600201546060808301829052860151919250613cd59190614dbd565b925083604001518160400151613ceb9190614dbd565b915050613d17565b6060830151613d029043614dbd565b9150826040015142613d149190614dbd565b90505b60408301518215613d54578284606001518c613d339190614dbd565b613d3d9084614e9f565b613d479190614ebe565b613d519082614df1565b90505b6040870151613d639082614dbd565b8760200151613d729190615012565b87518890613d819083906150a7565b600f90810b90915288516000910b129050613db157505093516001600160801b03169650610dc695505050505050565b60009950505050505050505050610dc6565b60008082815b6080811015613e3f5781831015613e3f5760006002613de88486614df1565b613df3906001614df1565b613dfd9190614ebe565b6000818152600460205260409020600201549091508710613e2057809350613e2e565b613e2b600182614dbd565b92505b50613e3881614e09565b9050613dc9565b5090949350505050565b6000818152600860205260409020546001600160a01b03838116911614613e7257613e72614d85565b6000818152600a60205260409020546001600160a01b031615613eac576000818152600a6020526040902080546001600160a01b03191690555b5050565b6000818152600860205260409020546001600160a01b03838116911614613ed957613ed9614d85565b600081815260086020526040902080546001600160a01b0319169055613eff82826146e5565b6001600160a01b0382166000908152600960205260408120805460019290613f28908490614dbd565b90915550505050565b816001600160a01b0316836001600160a01b031614158015613f535750600081115b15610f82576001600160a01b038316156140d4576001600160a01b03831660009081526019602052604081205463ffffffff169081613fb7576001600160a01b03851660009081526018602090815260408083208380529091529020600101613ff9565b6001600160a01b038516600090815260186020526040812090613fdb600185614d11565b63ffffffff1663ffffffff1681526020019081526020016000206001015b90506000614006866147a4565b6001600160a01b038716600090815260186020908152604080832063ffffffff8516845290915281209192506001909101905b835481101561409357600084828154811061405657614056614d6f565b90600052602060002001549050868114614080578254600181018455600084815260209020018190555b508061408b81614e09565b915050614039565b5061409f8460016151d9565b6001600160a01b0388166000908152601960205260409020805463ffffffff191663ffffffff92909216919091179055505050505b6001600160a01b03821615610f82576001600160a01b03821660009081526019602052604081205463ffffffff169081614133576001600160a01b03841660009081526018602090815260408083208380529091529020600101614175565b6001600160a01b038416600090815260186020526040812090614157600185614d11565b63ffffffff1663ffffffff1681526020019081526020016000206001015b90506000614182856147a4565b6001600160a01b038616600090815260186020908152604080832063ffffffff8516845290915290208354919250600190810191610400916141c49190614df1565b11156141e25760405162461bcd60e51b815260040161100490615201565b60005b835481101561423457600084828154811061420257614202614d6f565b60009182526020808320909101548554600181018755868452919092200155508061422c81614e09565b9150506141e5565b508054600181810183556000838152602090209091018690556142589085906151d9565b6001600160a01b0387166000908152601960205260409020805463ffffffff9290921663ffffffff1990921691909117905550505050505050565b6000818152600860205260409020546001600160a01b0316156142b8576142b8614d85565b600081815260086020908152604080832080546001600160a01b0319166001600160a01b03871690811790915580845260098084528285208054600d86528487208188528652848720889055878752600e865293862093909355908452909152805460019290613f28908490614df1565b806001600160a01b0316826001600160a01b031614610f82576001600160a01b038216156144dc576001600160a01b03821660009081526019602052604081205463ffffffff1690816143a1576001600160a01b038416600090815260186020908152604080832083805290915290206001016143e3565b6001600160a01b0384166000908152601860205260408120906143c5600185614d11565b63ffffffff1663ffffffff1681526020019081526020016000206001015b905060006143f0856147a4565b6001600160a01b038616600090815260186020908152604080832063ffffffff8516845290915281209192506001909101905b835481101561449b57600084828154811061444057614440614d6f565b600091825260208083209091015480835260089091526040909120549091506001600160a01b03908116908a1614614488578254600181018455600084815260209020018190555b508061449381614e09565b915050614423565b506144a78460016151d9565b6001600160a01b0387166000908152601960205260409020805463ffffffff191663ffffffff92909216919091179055505050505b6001600160a01b03811615610f82576001600160a01b03811660009081526019602052604081205463ffffffff16908161453b576001600160a01b0383166000908152601860209081526040808320838052909152902060010161457d565b6001600160a01b03831660009081526018602052604081209061455f600185614d11565b63ffffffff1663ffffffff1681526020019081526020016000206001015b9050600061458a846147a4565b6001600160a01b03808616600090815260186020908152604080832063ffffffff861684528252808320938b16835260099091529020548454929350600190910191610400906145db908390614df1565b11156145f95760405162461bcd60e51b815260040161100490615201565b60005b845481101561464b57600085828154811061461957614619614d6f565b60009182526020808320909101548654600181018855878452919092200155508061464381614e09565b9150506145fc565b5060005b8181101561469d576001600160a01b0389166000908152600d60209081526040808320848452825282205485546001810187558684529190922001558061469581614e09565b91505061464f565b506146a98560016151d9565b6001600160a01b0387166000908152601960205260409020805463ffffffff9290921663ffffffff199092169190911790555050505050505050565b6001600160a01b03821660009081526009602052604081205461470a90600190614dbd565b6000838152600e6020526040902054909150808203614759576001600160a01b0384166000908152600d602090815260408083208584528252808320839055858352600e909152812055612339565b6001600160a01b03939093166000908152600d6020908152604080832093835292815282822080548684528484208190558352600e9091528282209490945592839055908252812055565b6001600160a01b038116600090815260196020526040812054429063ffffffff16801580159061480d57506001600160a01b038416600090815260186020526040812083916147f4600185614d11565b63ffffffff168152602081019190915260400160002054145b1561136657611b15600182614d11565b6001600160e01b031981168114611a4857600080fd5b60006020828403121561484557600080fd5b81356113668161481d565b60005b8381101561486b578181015183820152602001614853565b838111156123395750506000910152565b60008151808452614894816020860160208601614850565b601f01601f19169290920160200192915050565b602081526000611366602083018461487c565b80356001600160a01b03811681146148d257600080fd5b919050565b600080604083850312156148ea57600080fd5b6148f3836148bb565b946020939093013593505050565b60006020828403121561491357600080fd5b5035919050565b60006020828403121561492c57600080fd5b611366826148bb565b6000806040838503121561494857600080fd5b50508035926020909101359150565b60008060006060848603121561496c57600080fd5b614975846148bb565b9250614983602085016148bb565b9150604084013590509250925092565b600080604083850312156149a657600080fd5b6149af836148bb565b91506149bd602084016148bb565b90509250929050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715614a0557614a056149c6565b604052919050565b60008060408385031215614a2057600080fd5b823567ffffffffffffffff80821115614a3857600080fd5b818501915085601f830112614a4c57600080fd5b8135602082821115614a6057614a606149c6565b8160051b9250614a718184016149dc565b8281529284018101928181019089851115614a8b57600080fd5b948201945b84861015614aa957853582529482019490820190614a90565b9997909101359750505050505050565b600081518084526020808501945080840160005b83811015614ae957815187529582019590820190600101614acd565b509495945050505050565b604081526000614b076040830185614ab9565b82810360208401526139e08185614ab9565b8015158114611a4857600080fd5b60008060408385031215614b3a57600080fd5b614b43836148bb565b91506020830135614b5381614b19565b809150509250929050565b600067ffffffffffffffff821115614b7857614b786149c6565b50601f01601f191660200190565b60008060008060808587031215614b9c57600080fd5b614ba5856148bb565b9350614bb3602086016148bb565b925060408501359150606085013567ffffffffffffffff811115614bd657600080fd5b8501601f81018713614be757600080fd5b8035614bfa614bf582614b5e565b6149dc565b818152886020838501011115614c0f57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b60008060008060008060c08789031215614c4a57600080fd5b614c53876148bb565b95506020870135945060408701359350606087013560ff81168114614c7757600080fd5b9598949750929560808101359460a0909101359350915050565b600080600060608486031215614ca657600080fd5b8335925060208401359150614cbd604085016148bb565b90509250925092565b60008060408385031215614cd957600080fd5b614ce2836148bb565b9150602083013563ffffffff81168114614b5357600080fd5b634e487b7160e01b600052601160045260246000fd5b600063ffffffff83811690831681811015614d2e57614d2e614cfb565b039392505050565b634e487b7160e01b600052601260045260246000fd5b600063ffffffff80841680614d6357614d63614d36565b92169190910492915050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b602080825260089082015267185d1d1858da195960c21b604082015260600190565b600082821015614dcf57614dcf614cfb565b500390565b600060208284031215614de657600080fd5b815161136681614b19565b60008219821115614e0457614e04614cfb565b500190565b600060018201614e1b57614e1b614cfb565b5060010190565b60208082526026908201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060408201526566757475726560d01b606082015260800190565b6020808252601e908201527f566f74696e67206c6f636b2063616e2062652032207965617273206d61780000604082015260600190565b6000816000190483118215151615614eb957614eb9614cfb565b500290565b600082614ecd57614ecd614d36565b500490565b60208082526024908201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686040820152636472617760e01b606082015260800190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090611d059083018461487c565b600060208284031215614f5b57600080fd5b81516113668161481d565b600060208284031215614f7857600080fd5b815167ffffffffffffffff811115614f8f57600080fd5b8201601f81018413614fa057600080fd5b8051614fae614bf582614b5e565b818152856020838501011115614fc357600080fd5b6139e0826020830160208601614850565b600081600f0b83600f0b80614feb57614feb614d36565b60016001607f1b031982146000198214161561500957615009614cfb565b90059392505050565b600081600f0b83600f0b60016001607f1b0360008213600084138383048511828216161561504257615042614cfb565b60016001607f1b0319600085128281168783058712161561506557615065614cfb565b6000871292508582058712848416161561508157615081614cfb565b8585058712818416161561509757615097614cfb565b5050509290910295945050505050565b600081600f0b83600f0b600081128160016001607f1b0319018312811516156150d2576150d2614cfb565b8160016001607f1b030183138116156150ed576150ed614cfb565b5090039392505050565b600081600f0b83600f0b600082128260016001607f1b030382138115161561512157615121614cfb565b8260016001607f1b031903821281161561513d5761513d614cfb565b50019392505050565b60008083128015600160ff1b85018412161561516457615164614cfb565b6001600160ff1b038401831381161561517f5761517f614cfb565b50500390565b634e487b7160e01b600052602160045260246000fd5b8481526020810184905260808101600684106151c757634e487b7160e01b600052602160045260246000fd5b60408201939093526060015292915050565b600063ffffffff8083168185168083038211156151f8576151f8614cfb565b01949350505050565b60208082526023908201527f64737452657020776f756c64206861766520746f6f206d616e7920746f6b656e60408201526249647360e81b60608201526080019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220091a36a76d9fb3254220f5ea90976bb610ed89524b2ee87668b41bf37c70394a64736f6c634300080d0033000000000000000000000000a04bc7140c26fc9bb1f36b1a604c7a5a88fb0e70

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061043e5760003560e01c80637116c60c11610236578063bfe109281161013b578063e441135c116100c3578063f1127ed811610087578063f1127ed814610bbe578063f8a0576314610be9578063fbd3a29d14610c0c578063fc0c546a14610c1f578063fd4a77f114610c4657600080fd5b8063e441135c14610b15578063e7a324dc14610b35578063e7e242d414610b5c578063e985e9c514610b6f578063ee99fe2814610bab57600080fd5b8063c87b56dd1161010a578063c87b56dd14610a8b578063d1c2babb14610a9e578063d1febfb914610ab1578063d4e54c3b14610aef578063e0514aba14610b0257600080fd5b8063bfe1092814610a4a578063c1f0fb9f14610a5d578063c2c4c5c114610a70578063c3cda52014610a7857600080fd5b806395d89b41116101be578063a183af521161018d578063a183af52146109aa578063a22cb465146109bd578063a4d855df146109d0578063b45a3c0e146109e3578063b88d4fde14610a3757600080fd5b806395d89b411461094c578063981b24d014610971578063986b7d8a146109845780639ab24eb01461099757600080fd5b806385f2aef21161020557806385f2aef2146108e75780638c2c9baf146108fa5780638e539e8c1461090d5780638fbb38ff14610920578063900cf0cf1461094357600080fd5b80637116c60c1461087e578063711974841461089157806375619ab5146108b45780637ecebe00146108c757600080fd5b806335cf668a116103475780635594a045116102cf5780636352211e116102935780636352211e146107d457806365fc3873146107fd5780636f548837146108255780636fcfff451461084557806370a082311461086b57600080fd5b80635594a0451461077157806356afe74414610784578063587cde1e146107a55780635c19a95c146107b85780635f5b0c32146107cb57600080fd5b8063461f711c11610316578063461f711c146106e857806346c96aac1461070e578063485cc955146107275780634bc2a6571461073a57806354fd4d501461074d57600080fd5b806335cf668a1461068c5780633a46b1a8146106af57806342842e0e146106c2578063430c2081146106d557600080fd5b806318160ddd116103ca57806325a58b561161039957806325a58b56146106105780632e1a7d4d146106165780632e720f7d146106295780632f745c591461063c578063313ce5671461067257600080fd5b806318160ddd146105bb5780631c984bc3146105c357806320606b70146105d657806323b872dd146105fd57600080fd5b8063081812fc11610411578063081812fc146104f7578063095cf5c614610538578063095ea7b31461054d5780630d6a2033146105605780631376f3da1461058057600080fd5b806301ffc9a714610443578063047fc9aa1461048557806306fdde031461049c5780630758c7d8146104cf575b600080fd5b610470610451366004614833565b6001600160e01b03191660009081526005602052604090205460ff1690565b60405190151581526020015b60405180910390f35b61048e60145481565b60405190815260200161047c565b6104c2604051806040016040528060078152602001660eccaa6eec2e0b60cb1b81525081565b60405161047c91906148a8565b6104e26104dd3660046148d7565b610c59565b60405163ffffffff909116815260200161047c565b610520610505366004614901565b6000908152600a60205260409020546001600160a01b031690565b6040516001600160a01b03909116815260200161047c565b61054b61054636600461491a565b610dcc565b005b61054b61055b3660046148d7565b610e05565b61048e61056e366004614901565b60156020526000908152604090205481565b61059361058e366004614935565b610eed565b60408051600f95860b81529390940b602084015292820152606081019190915260800161047c565b61048e610f34565b61048e6105d1366004614935565b610f44565b61048e7f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61054b61060b366004614957565b610f76565b4361048e565b61054b610624366004614901565b610f87565b61054b61063736600461491a565b611267565b61048e61064a3660046148d7565b6001600160a01b03919091166000908152600d60209081526040808320938352929052205490565b61067a601281565b60405160ff909116815260200161047c565b61048e61069a366004614901565b60009081526011602052604090206001015490565b61048e6106bd3660046148d7565b6112a0565b61054b6106d0366004614957565b61133f565b6104706106e33660046148d7565b61135a565b6106fb6106f6366004614901565b61136d565b604051600f9190910b815260200161047c565b600054610520906201000090046001600160a01b031681565b61054b610735366004614993565b6113b0565b61054b61074836600461491a565b611624565b6104c2604051806040016040528060058152602001640312e302e360dc1b81525081565b600254610520906001600160a01b031681565b610797610792366004614a0d565b611665565b60405161047c929190614af4565b6105206107b336600461491a565b6119fd565b61054b6107c636600461491a565b611a2d565b61048e61040081565b6105206107e2366004614901565b6000908152600860205260409020546001600160a01b031690565b61081061080b366004614935565b611a4b565b6040805192835260208301919091520161047c565b61048e610833366004614901565b600c6020526000908152604090205481565b6104e261085336600461491a565b60196020526000908152604090205463ffffffff1681565b61048e61087936600461491a565b611a9f565b61048e61088c366004614901565b611abd565b6106fb61089f366004614901565b601360205260009081526040902054600f0b81565b61054b6108c236600461491a565b611b1d565b61048e6108d536600461491a565b601a6020526000908152604090205481565b600154610520906001600160a01b031681565b61048e610908366004614935565b611b56565b61048e61091b366004614901565b611b62565b61047061092e366004614901565b60166020526000908152604090205460ff1681565b61048e60125481565b6104c2604051806040016040528060068152602001650eccaa6aea0f60d31b81525081565b61048e61097f366004614901565b611b6d565b61054b610992366004614901565b611d0f565b61048e6109a536600461491a565b611d59565b61048e6109b8366004614935565b611e2c565b61054b6109cb366004614b27565b611f4c565b61048e6109de366004614935565b611fd0565b610a176109f1366004614901565b601160205260009081526040902080546001820154600290920154600f9190910b919083565b60408051600f9490940b845260208401929092529082015260600161047c565b61054b610a45366004614b86565b6121a0565b600354610520906001600160a01b031681565b61054b610a6b366004614901565b61233f565b61054b612374565b61054b610a86366004614c31565b6123c2565b6104c2610a99366004614901565b61272b565b61048e610aac366004614935565b61286f565b610593610abf366004614901565b600460205260009081526040902080546001820154600290920154600f82810b93600160801b909304900b919084565b610810610afd366004614c91565b612a7c565b61048e610b10366004614935565b612ad1565b61048e610b23366004614901565b600f6020526000908152604090205481565b61048e7fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf81565b61048e610b6a366004614901565b612add565b610470610b7d366004614993565b6001600160a01b039182166000908152600b6020908152604080832093909416825291909152205460ff1690565b61048e610bb9366004614935565b612b05565b61048e610bcc366004614cc6565b601860209081526000928352604080842090915290825290205481565b61048e610bf7366004614901565b60009081526011602052604090206002015490565b61054b610c1a366004614901565b612bec565b6105207f000000000000000000000000a04bc7140c26fc9bb1f36b1a604c7a5a88fb0e7081565b61054b610c54366004614901565b612c23565b6001600160a01b03821660009081526019602052604081205463ffffffff16808203610c89576000915050610dc6565b6001600160a01b03841660009081526018602052604081208491610cae600185614d11565b63ffffffff16815260208101919091526040016000205411610cdd57610cd5600182614d11565b915050610dc6565b6001600160a01b0384166000908152601860209081526040808320838052909152902054831015610d12576000915050610dc6565b600080610d20600184614d11565b90505b8163ffffffff168163ffffffff161115610dc15760006002610d458484614d11565b610d4f9190614d4c565b610d599083614d11565b6001600160a01b038816600090815260186020908152604080832063ffffffff851684529091529020805491925090879003610d9b57509350610dc692505050565b8054871115610dac57819350610dba565b610db7600183614d11565b92505b5050610d23565b509150505b92915050565b6001546001600160a01b03163314610de357600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000818152600860205260409020546001600160a01b031680610e2757600080fd5b806001600160a01b0316836001600160a01b031603610e4557600080fd5b6000828152600860209081526040808320546001600160a01b038581168552600b845282852033808752945291909320549216149060ff168180610e865750805b610e8f57600080fd5b6000848152600a602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918716917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050505050565b601060205281600052604060002081633b9aca008110610f0c57600080fd5b6003020180546001820154600290920154600f82810b9550600160801b90920490910b925084565b6000610f3f42611abd565b905090565b600082815260106020526040812082633b9aca008110610f6657610f66614d6f565b6003020160010154905092915050565b610f8283838333612c5b565b505050565b60075460ff1660011901610f9a57600080fd5b6007805460ff19166002179055610fb13382612d2a565b610fbd57610fbd614d85565b600081815260156020526040902054158015610fe8575060008181526016602052604090205460ff16155b61100d5760405162461bcd60e51b815260040161100490614d9b565b60405180910390fd5b60008181526011602090815260409182902082516060810184528154600f0b8152600182015492810192909252600201549181018290529042101561108d5760405162461bcd60e51b8152602060048201526016602482015275546865206c6f636b206469646e27742065787069726560501b6044820152606401611004565b80516040805160608101825260008082526020808301828152838501838152888452601190925293909120915182546001600160801b0319166001600160801b03909116178255915160018201559051600290910155601454600f9190910b906110f78282614dbd565b601481905550611128848460405180606001604052806000600f0b8152602001600081526020016000815250612d8d565b60405163a9059cbb60e01b8152336004820152602481018390527f000000000000000000000000a04bc7140c26fc9bb1f36b1a604c7a5a88fb0e706001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015611195573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111b99190614dd4565b6111c5576111c5614d85565b6111ce8461343d565b60408051858152602081018490524281830152905133917f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca94919081900360600190a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c8161123c8482614dbd565b6040805192835260208301919091520160405180910390a150506007805460ff191660011790555050565b6001546001600160a01b0316331461127e57600080fd5b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6000806112ad8484610c59565b6001600160a01b038516600090815260186020908152604080832063ffffffff851684529091528120919250600190910190805b82548110156113355760008382815481106112fe576112fe614d6f565b9060005260206000200154905061131581886134fe565b61131f9084614df1565b925050808061132d90614e09565b9150506112e1565b5095945050505050565b610f82838383604051806020016040528060008152506121a0565b60006113668383612d2a565b9392505050565b6000818152600f60209081526040808320546010909252822081633b9aca00811061139a5761139a614d6f565b6003020154600160801b9004600f0b9392505050565b600054610100900460ff16158080156113d05750600054600160ff909116105b806113ea5750303b1580156113ea575060005460ff166001145b61144d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401611004565b6000805460ff191660011790558015611470576000805461ff0019166101001790555b600080546001600160a01b03808516620100000262010000600160b01b0319909216919091178255600180546001600160a01b0319908116331782556002805493881693909116929092179091556007805460ff199081168317909155437f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ee55427f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ed5560056020527fc01909ce2b517f8cd3a46ae0cfde9179f9b675cf633d3d84c8226585cc73c15680548216831790557f072ad3113145b5af48d301e3b9fc3bd1c97c3f26a14f5d44904b71469875631e8054821683179055635b5e139f60e01b83527f3b767bd59d7164fff7ec5b80ca1165d9d6e12ee8656896fac4159b0760bfd9f78054909116909117905560065460405190913091600080516020615245833981519152908290a46006546040516000903090600080516020615245833981519152908390a48015610f82576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b6001546001600160a01b0316331461163b57600080fd5b600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b6000818152601560205260409020546060908190158015611695575060008381526016602052604090205460ff16155b6116b15760405162461bcd60e51b815260040161100490614d9b565b6116bb3384612d2a565b6116c457600080fd5b6000838152600860209081526040808320546011835281842082516060810184528154600f0b808252600183015495820195909552600290910154928101929092526001600160a01b03169290911361171c57600080fd5b8051601454611734916001600160801b031690614dbd565b6014556000805b875182101561177d5787828151811061175657611756614d6f565b6020026020010151816117699190614df1565b90508161177581614e09565b92505061173b565b6040805160608082018352600080835260208084018281528486018381528d845260118352868420955186546001600160801b0319166001600160801b03909116178655905160018601555160029094019390935583519182018452808252918101829052918201526117f39088908590612d8d565b6117fc8761343d565b60408301514281116118205760405162461bcd60e51b815260040161100490614e22565b61182e6303c2670042614df1565b81111561184d5760405162461bcd60e51b815260040161100490614e68565b885167ffffffffffffffff811115611867576118676149c6565b604051908082528060200260200182016040528015611890578160200160208202803683370190505b509650885167ffffffffffffffff8111156118ad576118ad6149c6565b6040519080825280602002602001820160405280156118d6578160200160208202803683370190505b50955060008093505b89518410156119f0576006600081546118f790614e09565b90915550600654985061190a868a6135d2565b50828a858151811061191e5761191e614d6f565b602002602001015186600001516001600160801b031661193e9190614e9f565b6119489190614ebe565b60208087015160008c8152601183526040908190206001810183905581516060810183528154600f0b81529384019290925260029091015490820152909150611998908a90839085906005613631565b888885815181106119ab576119ab614d6f565b6020026020010181815250506119c189426134fe565b8785815181106119d3576119d3614d6f565b6020908102919091010152836119e881614e09565b9450506118df565b5050505050509250929050565b6001600160a01b038082166000908152601760205260408120549091168015611a265780611366565b5090919050565b6001600160a01b038116611a3e5750335b611a483382613881565b50565b600754600090819060ff1660011901611a6357600080fd5b6007805460ff19166002179055611a7b8484336138f4565b9150611a8782426134fe565b6007805460ff19166001179055919491935090915050565b6001600160a01b038116600090815260096020526040812054610dc6565b601254600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b93810193909352600181015491830191909152600201546060820152909190611b1581856139e9565b949350505050565b6001546001600160a01b03163314611b3457600080fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b60006113668383613aea565b6000610dc682611abd565b600043821115611b7f57611b7f614d85565b6012546000611b8e8483613dc3565b600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925083831015611c9d576000600481611bf1866001614df1565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606080830182905285015191925014611c975782606001518160600151611c5d9190614dbd565b83604001518260400151611c719190614dbd565b6060850151611c80908a614dbd565b611c8a9190614e9f565b611c949190614ebe565b91505b50611cec565b43826060015114611cec576060820151611cb79043614dbd565b6040830151611cc69042614dbd565b6060840151611cd59089614dbd565b611cdf9190614e9f565b611ce99190614ebe565b90505b611d0582828460400151611d009190614df1565b6139e9565b9695505050505050565b6000546201000090046001600160a01b03163314611d2c57600080fd5b600081815260156020526040902054611d4790600190614dbd565b60009182526015602052604090912055565b6001600160a01b03811660009081526019602052604081205463ffffffff16808203611d885750600092915050565b6001600160a01b038316600090815260186020526040812081611dac600185614d11565b63ffffffff1663ffffffff16815260200190815260200160002060010190506000805b8254811015611e23576000838281548110611dec57611dec614d6f565b90600052602060002001549050611e0381426134fe565b611e0d9084614df1565b9250508080611e1b90614e09565b915050611dcf565b50949350505050565b60075460009060ff1660011901611e4257600080fd5b6007805460ff19166002179055611e593384612d2a565b611e6557611e65614d85565b60008381526011602090815260409182902082516060810184528154600f0b8152600182015492810192909252600201549181019190915282611eaa57611eaa614d85565b60008160000151600f0b13611efa5760405162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b6044820152606401611004565b42816040015111611f1d5760405162461bcd60e51b815260040161100490614ed2565b611f2c84846000846002613631565b611f3684426134fe565b9150506007805460ff1916600117905592915050565b336001600160a01b03831603611f6457611f64614d85565b336000818152600b602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b60075460009060ff1660011901611fe657600080fd5b6007805460ff19166002179055611ffd3384612d2a565b61200957612009614d85565b600083815260116020908152604080832081516060810183528154600f0b815260018201549381019390935260020154908201529062093a808061204d8642614df1565b6120579190614ebe565b6120619190614e9f565b9050428260400151116120a55760405162461bcd60e51b815260206004820152600c60248201526b131bd8dac8195e1c1a5c995960a21b6044820152606401611004565b60008260000151600f0b136120f05760405162461bcd60e51b8152602060048201526011602482015270139bdd1a1a5b99c81a5cc81b1bd8dad959607a1b6044820152606401611004565b816040015181116121435760405162461bcd60e51b815260206004820152601f60248201527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006044820152606401611004565b6121516303c2670042614df1565b8111156121705760405162461bcd60e51b815260040161100490614e68565b61217f85600083856003613631565b61218985426134fe565b925050506007805460ff1916600117905592915050565b6121ac84848433612c5b565b823b1561233957604051630a85bd0160e11b81526001600160a01b0384169063150b7a02906121e5903390889087908790600401614f16565b6020604051808303816000875af1925050508015612220575060408051601f3d908101601f1916820190925261221d91810190614f49565b60015b6122c8573d80801561224e576040519150601f19603f3d011682016040523d82523d6000602084013e612253565b606091505b5080516000036122c05760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b6064820152608401611004565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b146123375760405162461bcd60e51b815260206004820152602660248201527f4552433732313a2045524337323152656365697665722072656a656374656420604482015265746f6b656e7360d01b6064820152608401611004565b505b50505050565b6000546201000090046001600160a01b0316331461235c57600080fd5b6000908152601660205260409020805460ff19169055565b6123c0600060405180606001604052806000600f0b815260200160008152602001600081525060405180606001604052806000600f0b8152602001600081526020016000815250612d8d565b565b336001600160a01b038716036123d757600080fd5b6001600160a01b0386166123ea57600080fd5b60408051808201825260078152660eccaa6eec2e0b60cb1b6020918201528151808301835260058152640312e302e360dc1b9082015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f1de6a1c189dcc4ee6a95fb1fdc3a550060accb8f042ce71a27c9dd46bee30732818401527f06c015bd22b4c69690933c1058878ebdfef31f9aaae40bbe86d8a09fe1b2972c60608201524660808201523060a0808301919091528351808303909101815260c0820184528051908301207fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60e08301526001600160a01b038a1661010083015261012082018990526101408083018990528451808403909101815261016083019094528351939092019290922061190160f01b61018084015261018283018290526101a2830181905290916000906101c20160408051601f198184030181528282528051602091820120600080855291840180845281905260ff8a169284019290925260608301889052608083018790529092509060019060a0016020604051602081039080840390855afa1580156125ab573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166126255760405162461bcd60e51b815260206004820152602e60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a20696e7660448201526d616c6964207369676e617475726560901b6064820152608401611004565b6001600160a01b0381166000908152601a6020526040812080549161264983614e09565b9190505589146126ae5760405162461bcd60e51b815260206004820152602a60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a20696e76604482015269616c6964206e6f6e636560b01b6064820152608401611004565b874211156127155760405162461bcd60e51b815260206004820152602e60248201527f566f74696e67457363726f773a3a64656c656761746542795369673a2073696760448201526d1b985d1d5c9948195e1c1a5c995960921b6064820152608401611004565b61271f818b613881565b50505050505050505050565b6000818152600860205260409020546060906001600160a01b03166127925760405162461bcd60e51b815260206004820152601b60248201527f517565727920666f72206e6f6e6578697374656e7420746f6b656e00000000006044820152606401611004565b60008281526011602090815260409182902082516060810184528154600f0b81526001820154928101929092526002908101549282019290925290546001600160a01b031663dd9ec149846127e781426134fe565b604080860151865191516001600160e01b031960e087901b16815261282a949392600f0b9060040193845260208401929092526040830152606082015260800190565b600060405180830381865afa158015612847573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113669190810190614f66565b60008281526015602052604081205415801561289a575060008381526016602052604090205460ff16155b6128b65760405162461bcd60e51b815260040161100490614d9b565b8183036128c257600080fd5b6128cc3384612d2a565b6128d557600080fd5b6128df3383612d2a565b6128e857600080fd5b60008381526011602081815260408084208151606080820184528254600f90810b8352600180850154848801526002948501548487019081528b8a529787529785902085519283018652805490910b825296870154948101949094529401549082015290514210801561295e5750428160400151115b61296757600080fd5b60008260000151600f0b9050600082604001518460400151101561298f578260400151612995565b83604001515b90508260200151846020015110156129b15783602001516129b7565b82602001515b6020848101919091526040805160608101825260008082528184018181528284018281528c83526011909552929020905181546001600160801b0319166001600160801b03909116178155905160018201559051600290910155601454612a1f908390614dbd565b601481905550612a50878560405180606001604052806000600f0b8152602001600081526020016000815250612d8d565b612a598761343d565b612a67868383866004613631565b612a7186426134fe565b979650505050505050565b600754600090819060ff1660011901612a9457600080fd5b6007805460ff19166002179055612aac8585856138f4565b9150612ab882426134fe565b6007805460ff1916600117905591959194509092505050565b600061136683836134fe565b6000818152600c6020526040812054439003612afb57506000919050565b610dc682426134fe565b60075460009060ff1660011901612b1b57600080fd5b6007805460ff1916600290811790915560008481526011602090815260409182902082516060810184528154600f0b8152600182015492810192909252909201549082015282612b6a57600080fd5b60008160000151600f0b13612bba5760405162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b6044820152606401611004565b42816040015111612bdd5760405162461bcd60e51b815260040161100490614ed2565b611f2c84846000846000613631565b6000546201000090046001600160a01b03163314612c0957600080fd5b600081815260156020526040902054611d47906001614df1565b6000546201000090046001600160a01b03163314612c4057600080fd5b6000908152601660205260409020805460ff19166001179055565b600082815260156020526040902054158015612c86575060008281526016602052604090205460ff16155b612ca25760405162461bcd60e51b815260040161100490614d9b565b612cac8183612d2a565b612cb557600080fd5b612cbf8483613e49565b612cc98483613eb0565b612ce4612cd5856119fd565b612cde856119fd565b84613f31565b612cee8383614293565b6000828152600c60205260408082204390555183916001600160a01b03808716929088169160008051602061524583398151915291a450505050565b600081815260086020908152604080832054600a8352818420546001600160a01b03918216808652600b855283862088841680885295529285205492938085149392909116149060ff168280612d7d5750815b80612a7157509695505050505050565b60408051608081018252600080825260208201819052918101829052606081019190915260408051608081018252600080825260208201819052918101829052606081019190915260125460009081908715612ef157428760400151118015612dfd575060008760000151600f0b135b15612e40578651612e13906303c2670090614fd4565b600f0b60208601526040870151612e2b904290614dbd565b8560200151612e3a9190615012565b600f0b85525b428660400151118015612e5a575060008660000151600f0b135b15612e9d578551612e70906303c2670090614fd4565b600f0b60208501526040860151612e88904290614dbd565b8460200151612e979190615012565b600f0b84525b604080880151600090815260136020528190205490870151600f9190910b935015612ef1578660400151866040015103612ed957829150612ef1565b604080870151600090815260136020522054600f0b91505b604080516080810182526000808252602082015242918101919091524360608201528115612f66575060008181526004602090815260409182902082516080810184528154600f81810b8352600160801b909104900b9281019290925260018101549282019290925260029091015460608201525b604081015181600042831015612fb3576040840151612f859042614dbd565b6060850151612f949043614dbd565b612fa690670de0b6b3a7640000614e9f565b612fb09190614ebe565b90505b600062093a80612fc38186614ebe565b612fcd9190614e9f565b905060005b60ff81101561314757612fe862093a8083614df1565b9150600042831115612ffc57429250613010565b50600082815260136020526040902054600f0b5b61301a8684614dbd565b87602001516130299190615012565b875188906130389083906150a7565b600f0b9052506020870180518291906130529083906150f7565b600f90810b90915288516000910b1215905061306d57600087525b60008760200151600f0b121561308557600060208801525b60408088018490528501519295508592670de0b6b3a7640000906130a99085614dbd565b6130b39086614e9f565b6130bd9190614ebe565b85606001516130cc9190614df1565b60608801526130dc600189614df1565b97504283036130f15750436060870152613147565b6000888152600460209081526040918290208951918a01516001600160801b03908116600160801b02921691909117815590880151600182015560608801516002909101555061314081614e09565b9050612fd2565b505060128590558b156131d2578860200151886020015161316891906150a7565b8460200181815161317991906150f7565b600f0b9052508851885161318d91906150a7565b8451859061319c9083906150f7565b600f90810b90915260208601516000910b121590506131bd57600060208501525b60008460000151600f0b12156131d257600084525b6000858152600460209081526040918290208651918701516001600160801b03908116600160801b02921691909117815590850151600182015560608501516002909101558b1561342f57428b60400151111561328657602089015161323890886150f7565b96508a604001518a604001510361325b57602088015161325890886150a7565b96505b60408b810151600090815260136020522080546001600160801b0319166001600160801b0389161790555b428a6040015111156132de578a604001518a6040015111156132de5760208801516132b190876150a7565b60408b810151600090815260136020522080546001600160801b0319166001600160801b03831617905595505b60008c8152600f60205260408120546132f8906001614df1565b905080600f60008f815260200190815260200160002081905550428960400181815250504389606001818152505088601060008f815260200190815260200160002082633b9aca00811061334e5761334e614d6f565b825160208401516001600160801b03908116600160801b0291161760039190910291909101908155604082015160018201556060909101516002909101557f8d83e324303a2f547eb8572fb6b42fb04620c4a95f09c9a1c8c6e5ac8f4624ed6133cc8e6000908152600860205260409020546001600160a01b031690565b8e8b600001518c602001518d604001518e60600151604051613425969594939291906001600160a01b039690961686526020860194909452600f92830b6040860152910b6060840152608083015260a082015260c00190565b60405180910390a1505b505050505050505050505050565b6134473382612d2a565b6134935760405162461bcd60e51b815260206004820181905260248201527f63616c6c6572206973206e6f74206f776e6572206e6f7220617070726f7665646044820152606401611004565b6000818152600860205260408120546001600160a01b0316906134b69083610e05565b6134ca6134c2826119fd565b600084613f31565b6134d48183613eb0565b60405182906000906001600160a01b03841690600080516020615245833981519152908390a45050565b6000828152600f602052604081205480820361351e576000915050610dc6565b600084815260106020526040812082633b9aca00811061354057613540614d6f565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b60208301526001810154928201839052600201546060820152915061358c9085615146565b816020015161359b9190615012565b815182906135aa9083906150a7565b600f90810b90915282516000910b121590506135c557600081525b51600f0b9150610dc69050565b60006001600160a01b0383166135ea576135ea614d85565b6135f86000612cde856119fd565b6136028383614293565b60405182906001600160a01b03851690600090600080516020615245833981519152908290a450600192915050565b60145482906136408682614df1565b601455604080516060810182526000808252602080830182815283850192835286519187015194870151909252929052600f9190910b81528251879084906136899083906150f7565b600f0b905250851561369d57604083018690525b82602001516000036136b0574260208401525b600088815260116020908152604091829020855181546001600160801b0319166001600160801b03909116178155908501516001820155908401516002909101556136fc888285612d8d565b33871580159061371e5750600485600581111561371b5761371b615185565b14155b801561373c5750600585600581111561373957613739615185565b14155b156137e6576040516323b872dd60e01b81526001600160a01b038281166004830152306024830152604482018a90527f000000000000000000000000a04bc7140c26fc9bb1f36b1a604c7a5a88fb0e7016906323b872dd906064016020604051808303816000875af11580156137b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137da9190614dd4565b6137e6576137e6614d85565b8360400151816001600160a01b03167fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6248b8b894260405161382a949392919061519b565b60405180910390a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c8361385e8a82614df1565b6040805192835260208301919091520160405180910390a1505050505050505050565b600061388c836119fd565b6001600160a01b0384811660008181526017602052604080822080546001600160a01b031916888616908117909155905194955093928516927f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4610f82838284614329565b60008062093a80806139068642614df1565b6139109190614ebe565b61391a9190614e9f565b90506000851161392957600080fd5b4281116139485760405162461bcd60e51b815260040161100490614e22565b6139566303c2670042614df1565b8111156139755760405162461bcd60e51b815260040161100490614e68565b60066000815461398490614e09565b9091555060065461399584826135d2565b5060008181526011602090815260409182902082516060810184528154600f0b815260018083015493820193909352600290910154928101929092526139e091839189918691613631565b95945050505050565b600080839050600062093a80808360400151613a059190614ebe565b613a0f9190614e9f565b905060005b60ff811015613ac257613a2a62093a8083614df1565b9150600085831115613a3e57859250613a52565b50600082815260136020526040902054600f0b5b6040840151613a619084614dbd565b8460200151613a709190615012565b84518590613a7f9083906150a7565b600f0b905250858303613a925750613ac2565b8084602001818151613aa491906150f7565b600f0b9052505060408301829052613abb81614e09565b9050613a14565b5060008260000151600f0b1215613ad857600082525b50516001600160801b03169392505050565b600043821115613afc57613afc614d85565b6000838152600f6020526040812054815b6080811015613b9c5781831015613b9c5760006002613b2c8486614df1565b613b37906001614df1565b613b419190614ebe565b6000888152601060205260409020909150869082633b9aca008110613b6857613b68614d6f565b600302016002015411613b7d57809350613b8b565b613b88600182614dbd565b92505b50613b9581614e09565b9050613b0d565b50600085815260106020526040812083633b9aca008110613bbf57613bbf614d6f565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b602083015260018101549282019290925260029091015460608201526012549091506000613c148783613dc3565b600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608201529192508084841015613cf3576000600481613c78876001614df1565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b93810193909352600181015491830191909152600201546060808301829052860151919250613cd59190614dbd565b925083604001518160400151613ceb9190614dbd565b915050613d17565b6060830151613d029043614dbd565b9150826040015142613d149190614dbd565b90505b60408301518215613d54578284606001518c613d339190614dbd565b613d3d9084614e9f565b613d479190614ebe565b613d519082614df1565b90505b6040870151613d639082614dbd565b8760200151613d729190615012565b87518890613d819083906150a7565b600f90810b90915288516000910b129050613db157505093516001600160801b03169650610dc695505050505050565b60009950505050505050505050610dc6565b60008082815b6080811015613e3f5781831015613e3f5760006002613de88486614df1565b613df3906001614df1565b613dfd9190614ebe565b6000818152600460205260409020600201549091508710613e2057809350613e2e565b613e2b600182614dbd565b92505b50613e3881614e09565b9050613dc9565b5090949350505050565b6000818152600860205260409020546001600160a01b03838116911614613e7257613e72614d85565b6000818152600a60205260409020546001600160a01b031615613eac576000818152600a6020526040902080546001600160a01b03191690555b5050565b6000818152600860205260409020546001600160a01b03838116911614613ed957613ed9614d85565b600081815260086020526040902080546001600160a01b0319169055613eff82826146e5565b6001600160a01b0382166000908152600960205260408120805460019290613f28908490614dbd565b90915550505050565b816001600160a01b0316836001600160a01b031614158015613f535750600081115b15610f82576001600160a01b038316156140d4576001600160a01b03831660009081526019602052604081205463ffffffff169081613fb7576001600160a01b03851660009081526018602090815260408083208380529091529020600101613ff9565b6001600160a01b038516600090815260186020526040812090613fdb600185614d11565b63ffffffff1663ffffffff1681526020019081526020016000206001015b90506000614006866147a4565b6001600160a01b038716600090815260186020908152604080832063ffffffff8516845290915281209192506001909101905b835481101561409357600084828154811061405657614056614d6f565b90600052602060002001549050868114614080578254600181018455600084815260209020018190555b508061408b81614e09565b915050614039565b5061409f8460016151d9565b6001600160a01b0388166000908152601960205260409020805463ffffffff191663ffffffff92909216919091179055505050505b6001600160a01b03821615610f82576001600160a01b03821660009081526019602052604081205463ffffffff169081614133576001600160a01b03841660009081526018602090815260408083208380529091529020600101614175565b6001600160a01b038416600090815260186020526040812090614157600185614d11565b63ffffffff1663ffffffff1681526020019081526020016000206001015b90506000614182856147a4565b6001600160a01b038616600090815260186020908152604080832063ffffffff8516845290915290208354919250600190810191610400916141c49190614df1565b11156141e25760405162461bcd60e51b815260040161100490615201565b60005b835481101561423457600084828154811061420257614202614d6f565b60009182526020808320909101548554600181018755868452919092200155508061422c81614e09565b9150506141e5565b508054600181810183556000838152602090209091018690556142589085906151d9565b6001600160a01b0387166000908152601960205260409020805463ffffffff9290921663ffffffff1990921691909117905550505050505050565b6000818152600860205260409020546001600160a01b0316156142b8576142b8614d85565b600081815260086020908152604080832080546001600160a01b0319166001600160a01b03871690811790915580845260098084528285208054600d86528487208188528652848720889055878752600e865293862093909355908452909152805460019290613f28908490614df1565b806001600160a01b0316826001600160a01b031614610f82576001600160a01b038216156144dc576001600160a01b03821660009081526019602052604081205463ffffffff1690816143a1576001600160a01b038416600090815260186020908152604080832083805290915290206001016143e3565b6001600160a01b0384166000908152601860205260408120906143c5600185614d11565b63ffffffff1663ffffffff1681526020019081526020016000206001015b905060006143f0856147a4565b6001600160a01b038616600090815260186020908152604080832063ffffffff8516845290915281209192506001909101905b835481101561449b57600084828154811061444057614440614d6f565b600091825260208083209091015480835260089091526040909120549091506001600160a01b03908116908a1614614488578254600181018455600084815260209020018190555b508061449381614e09565b915050614423565b506144a78460016151d9565b6001600160a01b0387166000908152601960205260409020805463ffffffff191663ffffffff92909216919091179055505050505b6001600160a01b03811615610f82576001600160a01b03811660009081526019602052604081205463ffffffff16908161453b576001600160a01b0383166000908152601860209081526040808320838052909152902060010161457d565b6001600160a01b03831660009081526018602052604081209061455f600185614d11565b63ffffffff1663ffffffff1681526020019081526020016000206001015b9050600061458a846147a4565b6001600160a01b03808616600090815260186020908152604080832063ffffffff861684528252808320938b16835260099091529020548454929350600190910191610400906145db908390614df1565b11156145f95760405162461bcd60e51b815260040161100490615201565b60005b845481101561464b57600085828154811061461957614619614d6f565b60009182526020808320909101548654600181018855878452919092200155508061464381614e09565b9150506145fc565b5060005b8181101561469d576001600160a01b0389166000908152600d60209081526040808320848452825282205485546001810187558684529190922001558061469581614e09565b91505061464f565b506146a98560016151d9565b6001600160a01b0387166000908152601960205260409020805463ffffffff9290921663ffffffff199092169190911790555050505050505050565b6001600160a01b03821660009081526009602052604081205461470a90600190614dbd565b6000838152600e6020526040902054909150808203614759576001600160a01b0384166000908152600d602090815260408083208584528252808320839055858352600e909152812055612339565b6001600160a01b03939093166000908152600d6020908152604080832093835292815282822080548684528484208190558352600e9091528282209490945592839055908252812055565b6001600160a01b038116600090815260196020526040812054429063ffffffff16801580159061480d57506001600160a01b038416600090815260186020526040812083916147f4600185614d11565b63ffffffff168152602081019190915260400160002054145b1561136657611b15600182614d11565b6001600160e01b031981168114611a4857600080fd5b60006020828403121561484557600080fd5b81356113668161481d565b60005b8381101561486b578181015183820152602001614853565b838111156123395750506000910152565b60008151808452614894816020860160208601614850565b601f01601f19169290920160200192915050565b602081526000611366602083018461487c565b80356001600160a01b03811681146148d257600080fd5b919050565b600080604083850312156148ea57600080fd5b6148f3836148bb565b946020939093013593505050565b60006020828403121561491357600080fd5b5035919050565b60006020828403121561492c57600080fd5b611366826148bb565b6000806040838503121561494857600080fd5b50508035926020909101359150565b60008060006060848603121561496c57600080fd5b614975846148bb565b9250614983602085016148bb565b9150604084013590509250925092565b600080604083850312156149a657600080fd5b6149af836148bb565b91506149bd602084016148bb565b90509250929050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715614a0557614a056149c6565b604052919050565b60008060408385031215614a2057600080fd5b823567ffffffffffffffff80821115614a3857600080fd5b818501915085601f830112614a4c57600080fd5b8135602082821115614a6057614a606149c6565b8160051b9250614a718184016149dc565b8281529284018101928181019089851115614a8b57600080fd5b948201945b84861015614aa957853582529482019490820190614a90565b9997909101359750505050505050565b600081518084526020808501945080840160005b83811015614ae957815187529582019590820190600101614acd565b509495945050505050565b604081526000614b076040830185614ab9565b82810360208401526139e08185614ab9565b8015158114611a4857600080fd5b60008060408385031215614b3a57600080fd5b614b43836148bb565b91506020830135614b5381614b19565b809150509250929050565b600067ffffffffffffffff821115614b7857614b786149c6565b50601f01601f191660200190565b60008060008060808587031215614b9c57600080fd5b614ba5856148bb565b9350614bb3602086016148bb565b925060408501359150606085013567ffffffffffffffff811115614bd657600080fd5b8501601f81018713614be757600080fd5b8035614bfa614bf582614b5e565b6149dc565b818152886020838501011115614c0f57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b60008060008060008060c08789031215614c4a57600080fd5b614c53876148bb565b95506020870135945060408701359350606087013560ff81168114614c7757600080fd5b9598949750929560808101359460a0909101359350915050565b600080600060608486031215614ca657600080fd5b8335925060208401359150614cbd604085016148bb565b90509250925092565b60008060408385031215614cd957600080fd5b614ce2836148bb565b9150602083013563ffffffff81168114614b5357600080fd5b634e487b7160e01b600052601160045260246000fd5b600063ffffffff83811690831681811015614d2e57614d2e614cfb565b039392505050565b634e487b7160e01b600052601260045260246000fd5b600063ffffffff80841680614d6357614d63614d36565b92169190910492915050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b602080825260089082015267185d1d1858da195960c21b604082015260600190565b600082821015614dcf57614dcf614cfb565b500390565b600060208284031215614de657600080fd5b815161136681614b19565b60008219821115614e0457614e04614cfb565b500190565b600060018201614e1b57614e1b614cfb565b5060010190565b60208082526026908201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060408201526566757475726560d01b606082015260800190565b6020808252601e908201527f566f74696e67206c6f636b2063616e2062652032207965617273206d61780000604082015260600190565b6000816000190483118215151615614eb957614eb9614cfb565b500290565b600082614ecd57614ecd614d36565b500490565b60208082526024908201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686040820152636472617760e01b606082015260800190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090611d059083018461487c565b600060208284031215614f5b57600080fd5b81516113668161481d565b600060208284031215614f7857600080fd5b815167ffffffffffffffff811115614f8f57600080fd5b8201601f81018413614fa057600080fd5b8051614fae614bf582614b5e565b818152856020838501011115614fc357600080fd5b6139e0826020830160208601614850565b600081600f0b83600f0b80614feb57614feb614d36565b60016001607f1b031982146000198214161561500957615009614cfb565b90059392505050565b600081600f0b83600f0b60016001607f1b0360008213600084138383048511828216161561504257615042614cfb565b60016001607f1b0319600085128281168783058712161561506557615065614cfb565b6000871292508582058712848416161561508157615081614cfb565b8585058712818416161561509757615097614cfb565b5050509290910295945050505050565b600081600f0b83600f0b600081128160016001607f1b0319018312811516156150d2576150d2614cfb565b8160016001607f1b030183138116156150ed576150ed614cfb565b5090039392505050565b600081600f0b83600f0b600082128260016001607f1b030382138115161561512157615121614cfb565b8260016001607f1b031903821281161561513d5761513d614cfb565b50019392505050565b60008083128015600160ff1b85018412161561516457615164614cfb565b6001600160ff1b038401831381161561517f5761517f614cfb565b50500390565b634e487b7160e01b600052602160045260246000fd5b8481526020810184905260808101600684106151c757634e487b7160e01b600052602160045260246000fd5b60408201939093526060015292915050565b600063ffffffff8083168185168083038211156151f8576151f8614cfb565b01949350505050565b60208082526023908201527f64737452657020776f756c64206861766520746f6f206d616e7920746f6b656e60408201526249647360e81b60608201526080019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220091a36a76d9fb3254220f5ea90976bb610ed89524b2ee87668b41bf37c70394a64736f6c634300080d0033

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

000000000000000000000000a04bc7140c26fc9bb1f36b1a604c7a5a88fb0e70

-----Decoded View---------------
Arg [0] : token_addr (address): 0xA04BC7140c26fc9BB1F36B1A604C7A5a88fb0E70

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000a04bc7140c26fc9bb1f36b1a604c7a5a88fb0e70


[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.