ETH Price: $2,956.06 (+0.98%)

Token

veNFT (veNFT)

Overview

Max Total Supply

3,604,512.370541547359190237 veNFT

Holders

980

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Balance
0.000000000000000001 veNFT
0x1b0649b83354a57c4c851092f20e7833ee848305
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.11+commit.d7f03943

Optimization Enabled:
Yes with 800 runs

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

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol';
import "./libraries/Base64.sol";

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

struct LockedBalance {
    int128 amount;
    uint end;
}

contract VotingEscrow is IERC721, IERC721Metadata {
    enum DepositType {
        DEPOSIT_FOR_TYPE,
        CREATE_LOCK_TYPE,
        INCREASE_LOCK_AMOUNT,
        INCREASE_UNLOCK_TIME,
        MERGE_TYPE
    }

    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);

    uint internal constant WEEK = 1 weeks;
    uint internal constant MAXTIME = 116_367_840; // 3.69 years
    int128 internal constant iMAXTIME = 116_367_840; // 3.69 years
    uint internal constant MULTIPLIER = 1 ether;

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

    mapping(uint => uint) public ownership_change;

    uint public epoch;
    mapping(uint => Point) public point_history; // epoch -> unsigned point
    mapping(uint => Point[1000000000]) public user_point_history; // user -> Point[user_epoch]

    mapping(uint => uint) public user_point_epoch;
    mapping(uint => int128) public slope_changes; // time -> signed slope change

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

    string constant public name = "veNFT";
    string constant public symbol = "veNFT";
    uint8 constant public decimals = 18;

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

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

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

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

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

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

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

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

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

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

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

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

    /// @notice Contract constructor
    /// @param token_addr `ERC20CRV` token address
    constructor(
        address token_addr
    ) {
        require(
            token_addr != address(0),
            "VotingEscrow: zero address provided in constructor"
        );
        token = token_addr;
        voter = msg.sender;
        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);
    }

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

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

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

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

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

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

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

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

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

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

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

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

    function _isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.
        uint size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

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

        // Check if recipient is contract
        if (_isContract(_to)) {
            bytes4 retval = IERC721Receiver(_to).onERC721Received(
                msg.sender,
                _from,
                _tokenId,
                _data
            );
            require(retval == IERC721Receiver.onERC721Received.selector, "ERC721: transfer rejected");
        }
    }

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

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

    /// @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));
        // Add NFT. Throws if `_tokenId` is owned by someone
        _addTokenTo(_to, _tokenId);
        if (_isContract(_to)) {
            bytes4 retval = IERC721Receiver(_to).onERC721Received(
                msg.sender,
                address(0),
                _tokenId,
                ''
            );
            require(retval == IERC721Receiver.onERC721Received.selector);
        }
        //require(_checkOnERC721Received(address(0), _to, _tokenId, ''), "Contract can't handle ERC721");

        emit Transfer(address(0), _to, _tokenId);
        return true;
    }

    /// @notice Record global and per-user data to checkpoint
    /// @param _tokenId NFT token ID. No user checkpoint if 0
    /// @param 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 / WEEK) * WEEK;
            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 += WEEK;
                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;
        }
    }

    /// @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.end) = (_locked.amount, _locked.end);
        // Adding to existing lock, or if a lock is expired - creating a new one
        _locked.amount += int128(int256(_value));
        if (unlock_time != 0) {
            _locked.end = unlock_time;
        }
        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) {
            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 setVoter(address _voter) external {
        require(msg.sender == voter);
        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 {
        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];
        uint value0 = uint(int256(_locked0.amount));
        uint end = _locked0.end >= _locked1.end ? _locked0.end : _locked1.end;

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

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

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

    /// @notice Deposit `_value` tokens for `_tokenId` and add to the lock
    /// @dev Anyone (even a smart contract) can deposit for someone else, but
    ///      cannot extend their locktime and deposit for a brand new user
    /// @param _tokenId lock NFT
    /// @param _value Amount to add to user's lock
    function deposit_for(uint _tokenId, uint _value) external nonreentrant {
        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);
    }

    /// @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) / WEEK * WEEK; // 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 3.69 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 `_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) {
        return _create_lock(_value, _lock_duration, _to);
    }

    /// @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) {
        return _create_lock(_value, _lock_duration, msg.sender);
    }

    /// @notice Deposit `_value` additional tokens for `_tokenId` without modifying the unlock time
    /// @param _value Amount of tokens to deposit and add to the lock
    function increase_amount(uint _tokenId, uint _value) external nonreentrant {
        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);
    }

    /// @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 {
        assert(_isApprovedOrOwner(msg.sender, _tokenId));

        LockedBalance memory _locked = locked[_tokenId];
        uint unlock_time = (block.timestamp + _lock_duration) / WEEK * WEEK; // 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 3.69 years max');

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

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

        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);
    }

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

    /// @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
        _tokenURI(
            _tokenId,
            _balanceOfNFT(_tokenId, block.timestamp),
            _locked.end,
            uint(int256(_locked.amount))
        );
    }

    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 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 / WEEK) * WEEK;
        for (uint i = 0; i < 255; ++i) {
            t_i += WEEK;
            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));
    }

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

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

    /// @notice Calculate total voting power at some point in the past
    /// @param _block Block to calculate the total voting power at
    /// @return Total voting power at `_block`
    function totalSupplyAt(uint _block) external view returns (uint) {
        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);
    }

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

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

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

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

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

        address owner = ownerOf(_tokenId);

        // Clear approval
        approve(address(0), _tokenId);
        // Remove token
        _removeTokenFrom(owner, _tokenId);
        emit Transfer(owner, address(0), _tokenId);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.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 be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

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

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

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

// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity ^0.8.4;

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

    function encode(bytes memory data) internal pure returns (string memory) {
        if (data.length == 0) return '';

        // load the table into memory
        string memory table = TABLE;

        // multiply by 4/3 rounded up
        uint256 encodedLen = 4 * ((data.length + 2) / 3);

        // add some extra buffer at the end required for the writing
        string memory result = new string(encodedLen + 32);

        assembly {
            // set the actual output length
            mstore(result, encodedLen)

            // prepare the lookup table
            let tablePtr := add(table, 1)

            // input ptr
            let dataPtr := data
            let endPtr := add(dataPtr, mload(data))

            // result ptr, jump over length
            let resultPtr := add(result, 32)

            // run over the input, 3 bytes at a time
            for {

            } lt(dataPtr, endPtr) {

            } {
                dataPtr := add(dataPtr, 3)

                // read 3 bytes
                let input := mload(dataPtr)

                // write 4 characters
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(6, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(input, 0x3F)))))
                resultPtr := add(resultPtr, 1)
            }

            // padding with '='
            switch mod(mload(data), 3)
            case 1 {
                mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
            }
            case 2 {
                mstore(sub(resultPtr, 1), shl(248, 0x3d))
            }
        }

        return result;
    }
}

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

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

Contract Security Audit

Contract ABI

API
[{"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":"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":"uint256","name":"prevSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"supply","type":"uint256"}],"name":"Supply","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"abstain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"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":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"create_lock","outputs":[{"internalType":"uint256","name":"","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":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"deposit_for","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"detach","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"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":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"increase_unlock_time","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isApprovedOrOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"locked","outputs":[{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint256","name":"end","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"locked__end","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"}],"name":"merge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"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":"_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":[],"name":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_tokenIndex","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"t","type":"uint256"}],"name":"totalSupplyAtT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"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":[{"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"}]

60a06040526013805460ff191660011790553480156200001e57600080fd5b5060405162004046380380620040468339810160408190526200004191620001f7565b6001600160a01b038116620000b75760405162461bcd60e51b815260206004820152603260248201527f566f74696e67457363726f773a207a65726f20616464726573732070726f76696044820152713232b21034b71031b7b739ba393ab1ba37b960711b606482015260840160405180910390fd5b6001600160a01b038116608052600a80546001600160a01b03191633179055437f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ee55427f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ed5560126020527f6afebfa21c417411ab7392a59bbb98ec35612153c5e8aeb3f64f4e016707b77a805460ff1990811660019081179092557fdf428ce7b59746cf2e618e63c6311d427105f2c3dc163ff0902907dea1728df58054821683179055635b5e139f60e01b60009081527fc0e00dda16840520b43384d85650edbbad8e5afcb041d20f42eb5aea304558cd80549092169092179055600b546040519091309160008051602062004026833981519152908290a4600b54604051600090309060008051602062004026833981519152908390a45062000229565b6000602082840312156200020a57600080fd5b81516001600160a01b03811681146200022257600080fd5b9392505050565b608051613dd362000253600039600081816108a301528181610c1a01526129010152613dd36000f3fe608060405234801561001057600080fd5b50600436106103415760003560e01c80638c2c9baf116101bd578063c87b56dd116100f9578063e7e242d4116100a2578063f8a057631161007c578063f8a0576314610867578063fbd3a29d1461088b578063fc0c546a1461089e578063fd4a77f1146108c557600080fd5b8063e7e242d414610805578063e985e9c514610818578063ee99fe281461085457600080fd5b8063d4e54c3b116100d3578063d4e54c3b146107bf578063e0514aba146107d2578063e441135c146107e557600080fd5b8063c87b56dd1461075b578063d1c2babb1461076e578063d1febfb91461078157600080fd5b8063a183af5211610166578063b45a3c0e11610140578063b45a3c0e146106e5578063b88d4fde1461072d578063c1f0fb9f14610740578063c2c4c5c11461075357600080fd5b8063a183af52146106ac578063a22cb465146106bf578063a4d855df146106d257600080fd5b806395d89b411161019757806395d89b411461039f578063981b24d014610686578063986b7d8a1461069957600080fd5b80638c2c9baf146106475780638fbb38ff1461065a578063900cf0cf1461067d57600080fd5b80632f745c591161028c5780634bc2a657116102355780636f5488371161020f5780636f548837146105de57806370a08231146105fe5780637116c60c14610611578063711974841461062457600080fd5b80634bc2a6571461058f5780636352211e146105a257806365fc3873146105cb57600080fd5b8063430c208111610266578063430c208114610543578063461f711c1461055657806346c96aac1461057c57600080fd5b80632f745c59146104e0578063313ce5671461051657806342842e0e1461053057600080fd5b80631376f3da116102ee57806323b872dd116102c857806323b872dd146104b457806325a58b56146104c75780632e1a7d4d146104cd57600080fd5b80631376f3da1461045e57806318160ddd146104995780631c984bc3146104a157600080fd5b8063081812fc1161031f578063081812fc146103e8578063095ea7b3146104295780630d6a20331461043e57600080fd5b806301ffc9a714610346578063047fc9aa1461038857806306fdde031461039f575b600080fd5b610373610354366004613207565b6001600160e01b03191660009081526012602052604090205460ff1690565b60405190151581526020015b60405180910390f35b61039160005481565b60405190815260200161037f565b6103db6040518060400160405280600581526020017f76654e465400000000000000000000000000000000000000000000000000000081525081565b60405161037f919061327c565b6104116103f636600461328f565b6000908152600d60205260409020546001600160a01b031690565b6040516001600160a01b03909116815260200161037f565b61043c6104373660046132c4565b6108d8565b005b61039161044c36600461328f565b60086020526000908152604090205481565b61047161046c3660046132ee565b6109c1565b60408051600f95860b81529390940b602084015292820152606081019190915260800161037f565b610391610a08565b6103916104af3660046132ee565b610a18565b61043c6104c2366004613310565b610a4b565b43610391565b61043c6104db36600461328f565b610a5c565b6103916104ee3660046132c4565b6001600160a01b03919091166000908152600f60209081526040808320938352929052205490565b61051e601281565b60405160ff909116815260200161037f565b61043c61053e366004613310565b610d3d565b6103736105513660046132c4565b610d58565b61056961056436600461328f565b610d6b565b604051600f9190910b815260200161037f565b600a54610411906001600160a01b031681565b61043c61059d36600461334c565b610dae565b6104116105b036600461328f565b6000908152600c60205260409020546001600160a01b031690565b6103916105d93660046132ee565b610de7565b6103916105ec36600461328f565b60026020526000908152604090205481565b61039161060c36600461334c565b610e29565b61039161061f36600461328f565b610e47565b61056961063236600461328f565b600760205260009081526040902054600f0b81565b6103916106553660046132ee565b610ea7565b61037361066836600461328f565b60096020526000908152604090205460ff1681565b61039160035481565b61039161069436600461328f565b610eb3565b61043c6106a736600461328f565b611055565b61043c6106ba3660046132ee565b611099565b61043c6106cd366004613375565b6111db565b61043c6106e03660046132ee565b611260565b6107136106f336600461328f565b60016020819052600091825260409091208054910154600f9190910b9082565b60408051600f9390930b835260208301919091520161037f565b61043c61073b3660046133c2565b611469565b61043c61074e36600461328f565b61155e565b61043c61158d565b6103db61076936600461328f565b6115cd565b61043c61077c3660046132ee565b61167a565b61047161078f36600461328f565b600460205260009081526040902080546001820154600290920154600f82810b93600160801b909304900b919084565b6103916107cd36600461349e565b611809565b6103916107e03660046132ee565b61184c565b6103916107f336600461328f565b60066020526000908152604090205481565b61039161081336600461328f565b611858565b6103736108263660046134d3565b6001600160a01b03918216600090815260116020908152604080832093909416825291909152205460ff1690565b61043c6108623660046132ee565b611880565b61039161087536600461328f565b6000908152600160208190526040909120015490565b61043c61089936600461328f565b611997565b6104117f000000000000000000000000000000000000000000000000000000000000000081565b61043c6108d336600461328f565b6119c8565b6000818152600c60205260409020546001600160a01b0316806108fa57600080fd5b806001600160a01b0316836001600160a01b0316141561091957600080fd5b6000828152600c60209081526040808320546001600160a01b0385811685526011845282852033808752945291909320549216149060ff16818061095a5750805b61096357600080fd5b6000848152600d602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918716917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050505050565b600560205281600052604060002081633b9aca0081106109e057600080fd5b6003020180546001820154600290920154600f82810b9550600160801b90920490910b925084565b6000610a1342610e47565b905090565b600082815260056020526040812082633b9aca008110610a3a57610a3a613506565b600302016001015490505b92915050565b610a57838383336119fa565b505050565b60135460ff16600114610a6e57600080fd5b6013805460ff19166002179055610a853382611adb565b610a9157610a9161351c565b600081815260086020526040902054158015610abc575060008181526009602052604090205460ff16155b610af85760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b60448201526064015b60405180910390fd5b60008181526001602081815260409283902083518085019094528054600f0b845290910154908201819052421015610b725760405162461bcd60e51b815260206004820152601660248201527f546865206c6f636b206469646e277420657870697265000000000000000000006044820152606401610aef565b805160408051808201825260008082526020808301828152878352600191829052938220925183546fffffffffffffffffffffffffffffffff19166001600160801b039091161783559251919092015554600f9190910b90610bd48282613548565b600081905550610bfe848460405180604001604052806000600f0b81526020016000815250611b41565b60405163a9059cbb60e01b8152336004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610c6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c8f919061355f565b610c9b57610c9b61351c565b610ca484612172565b60408051858152602081018490524281830152905133917f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca94919081900360600190a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c81610d128482613548565b6040805192835260208301919091520160405180910390a150506013805460ff191660011790555050565b610a5783838360405180602001604052806000815250611469565b6000610d648383611adb565b9392505050565b6000818152600660209081526040808320546005909252822081633b9aca008110610d9857610d98613506565b6003020154600160801b9004600f0b9392505050565b600a546001600160a01b03163314610dc557600080fd5b600a80546001600160a01b0319166001600160a01b0392909216919091179055565b60135460009060ff16600114610dfc57600080fd5b6013805460ff19166002179055610e14838333612231565b90506013805460ff1916600117905592915050565b6001600160a01b0381166000908152600e6020526040812054610a45565b600354600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b93810193909352600181015491830191909152600201546060820152909190610e9f81856123a3565b949350505050565b6000610d6483836124a5565b600043821115610ec557610ec561351c565b6003546000610ed4848361277d565b600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925083831015610fe3576000600481610f3786600161357c565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606080830182905285015191925014610fdd5782606001518160600151610fa39190613548565b83604001518260400151610fb79190613548565b6060850151610fc6908a613548565b610fd09190613594565b610fda91906135c9565b91505b50611032565b43826060015114611032576060820151610ffd9043613548565b604083015161100c9042613548565b606084015161101b9089613548565b6110259190613594565b61102f91906135c9565b90505b61104b82828460400151611046919061357c565b6123a3565b9695505050505050565b600a546001600160a01b0316331461106c57600080fd5b60008181526008602052604090205461108790600190613548565b60009182526008602052604090912055565b60135460ff166001146110ab57600080fd5b6013805460ff191660021790556110c23383611adb565b6110ce576110ce61351c565b60008281526001602081815260409283902083518085019094528054600f0b84529091015490820152816111045761110461351c565b60008160000151600f0b1361115b5760405162461bcd60e51b815260206004820152601660248201527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006044820152606401610aef565b428160200151116111ba5760405162461bcd60e51b8152602060048201526024808201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686044820152636472617760e01b6064820152608401610aef565b6111c983836000846002612807565b50506013805460ff1916600117905550565b6001600160a01b0382163314156111f4576111f461351c565b3360008181526011602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b60135460ff1660011461127257600080fd5b6013805460ff191660021790556112893383611adb565b6112955761129561351c565b600082815260016020818152604080842081518083019092528054600f0b825290920154908201529062093a80806112cd854261357c565b6112d791906135c9565b6112e19190613594565b9050428260200151116113365760405162461bcd60e51b815260206004820152600c60248201527f4c6f636b206578706972656400000000000000000000000000000000000000006044820152606401610aef565b60008260000151600f0b1361138d5760405162461bcd60e51b815260206004820152601160248201527f4e6f7468696e67206973206c6f636b65640000000000000000000000000000006044820152606401610aef565b816020015181116113e05760405162461bcd60e51b815260206004820152601f60248201527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006044820152606401610aef565b6113ee6306efa1e04261357c565b8111156114475760405162461bcd60e51b815260206004820152602160248201527f566f74696e67206c6f636b2063616e20626520332e3639207965617273206d616044820152600f60fb1b6064820152608401610aef565b61145684600083856003612807565b50506013805460ff191660011790555050565b611475848484336119fa565b823b1561155857604051630a85bd0160e11b81526000906001600160a01b0385169063150b7a02906114b19033908990889088906004016135dd565b6020604051808303816000875af11580156114d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f4919061360f565b90506001600160e01b03198116630a85bd0160e11b146115565760405162461bcd60e51b815260206004820152601960248201527f4552433732313a207472616e736665722072656a6563746564000000000000006044820152606401610aef565b505b50505050565b600a546001600160a01b0316331461157557600080fd5b6000908152600960205260409020805460ff19169055565b6115cb600060405180604001604052806000600f0b8152602001600081525060405180604001604052806000600f0b81526020016000815250611b41565b565b6000818152600c60205260409020546060906001600160a01b03166116345760405162461bcd60e51b815260206004820152601b60248201527f517565727920666f72206e6f6e6578697374656e7420746f6b656e00000000006044820152606401610aef565b60008281526001602081815260409283902083518085019094528054600f0b84529091015490820152610d648361166b8142612a15565b60208401518451600f0b612ae7565b6000828152600860205260409020541580156116a5575060008281526009602052604090205460ff16155b6116dc5760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b6044820152606401610aef565b808214156116e957600080fd5b6116f33383611adb565b6116fc57600080fd5b6117063382611adb565b61170f57600080fd5b6000828152600160208181526040808420815180830183528154600f90810b82529185015481850190815287875285855283872084518086019095528054840b855290950154938301849052805194519095929490910b92111561177757826020015161177d565b83602001515b604080518082018252600080825260208083018281528b83526001808352858420945185546fffffffffffffffffffffffffffffffff19166001600160801b0390911617855590519301929092558251808401909352808352908201529091506117ea9087908690611b41565b6117f386612172565b611801858383866004612807565b505050505050565b60135460009060ff1660011461181e57600080fd5b6013805460ff19166002179055611836848484612231565b90506013805460ff191660011790559392505050565b6000610d648383612a15565b60008181526002602052604081205443141561187657506000919050565b610a458242612a15565b60135460ff1660011461189257600080fd5b6013805460ff1916600217905560008281526001602081815260409283902083518085019094528054600f0b84529091015490820152816118d257600080fd5b60008160000151600f0b136119295760405162461bcd60e51b815260206004820152601660248201527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006044820152606401610aef565b428160200151116119885760405162461bcd60e51b8152602060048201526024808201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686044820152636472617760e01b6064820152608401610aef565b6111c983836000846000612807565b600a546001600160a01b031633146119ae57600080fd5b60008181526008602052604090205461108790600161357c565b600a546001600160a01b031633146119df57600080fd5b6000908152600960205260409020805460ff19166001179055565b600082815260086020526040902054158015611a25575060008281526009602052604090205460ff16155b611a5c5760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b6044820152606401610aef565b611a668183611adb565b611a6f57600080fd5b611a798483612c1e565b611a838483612c85565b611a8d8383612d06565b6000828152600260205260408082204390555183916001600160a01b0380871692908816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a450505050565b6000818152600c6020908152604080832054600d8352818420546001600160a01b039182168086526011855283862088841680885295529285205492938085149392909116149060ff168280611b2e5750815b80611b365750805b979650505050505050565b60408051608081018252600080825260208201819052918101829052606081019190915260408051608081018252600080825260208201819052918101829052606081019190915260035460009081908715611cad57428760200151118015611bb1575060008760000151600f0b135b15611bf6578651611bc7906306efa1e09061362c565b600f0b602080870191909152870151611be1904290613548565b8560200151611bf09190613673565b600f0b85525b428660200151118015611c10575060008660000151600f0b135b15611c55578551611c26906306efa1e09061362c565b600f0b602080860191909152860151611c40904290613548565b8460200151611c4f9190613673565b600f0b84525b602080880151600090815260078252604090205490870151600f9190910b935015611cad57866020015186602001511415611c9257829150611cad565b602080870151600090815260079091526040902054600f0b91505b604080516080810182526000808252602082015242918101919091524360608201528115611d22575060008181526004602090815260409182902082516080810184528154600f81810b8352600160801b909104900b9281019290925260018101549282019290925260029091015460608201525b604081015181600042831015611d6f576040840151611d419042613548565b6060850151611d509043613548565b611d6290670de0b6b3a7640000613594565b611d6c91906135c9565b90505b600062093a80611d7f81866135c9565b611d899190613594565b905060005b60ff811015611f0457611da462093a808361357c565b9150600042831115611db857429250611dcc565b50600082815260076020526040902054600f0b5b611dd68684613548565b8760200151611de59190613673565b87518890611df490839061371a565b600f0b905250602087018051829190611e0e90839061377c565b600f90810b90915288516000910b12159050611e2957600087525b60008760200151600f0b1215611e4157600060208801525b60408088018490528501519295508592670de0b6b3a764000090611e659085613548565b611e6f9086613594565b611e7991906135c9565b8560600151611e88919061357c565b6060880152611e9860018961357c565b975042831415611eae5750436060870152611f04565b6000888152600460209081526040918290208951918a01516001600160801b03908116600160801b029216919091178155908801516001820155606088015160029091015550611efd816137dd565b9050611d8e565b505060038590558b15611f8f5788602001518860200151611f25919061371a565b84602001818151611f36919061377c565b600f0b90525088518851611f4a919061371a565b84518590611f5990839061377c565b600f90810b90915260208601516000910b12159050611f7a57600060208501525b60008460000151600f0b1215611f8f57600084525b6000858152600460209081526040918290208651918701516001600160801b03908116600160801b02921691909117815590850151600182015560608501516002909101558b1561216457428b602001511115612050576020890151611ff5908861377c565b96508a602001518a602001511415612019576020880151612016908861371a565b96505b60208b810151600090815260079091526040902080546fffffffffffffffffffffffffffffffff19166001600160801b0389161790555b428a6020015111156120b4578a602001518a6020015111156120b457602088015161207b908761371a565b60208b810151600090815260079091526040902080546fffffffffffffffffffffffffffffffff19166001600160801b03831617905595505b60008c8152600660205260408120546120ce90600161357c565b905080600660008f815260200190815260200160002081905550428960400181815250504389606001818152505088600560008f815260200190815260200160002082633b9aca00811061212457612124613506565b825160208401516001600160801b03908116600160801b029116176003919091029190910190815560408201516001820155606090910151600290910155505b505050505050505050505050565b61217c3382611adb565b6121c85760405162461bcd60e51b815260206004820181905260248201527f63616c6c6572206973206e6f74206f776e6572206e6f7220617070726f7665646044820152606401610aef565b6000818152600c60205260408120546001600160a01b0316906121eb90836108d8565b6121f58183612c85565b60405182906000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b60008062093a8080612243864261357c565b61224d91906135c9565b6122579190613594565b90506000851161226657600080fd5b4281116122db5760405162461bcd60e51b815260206004820152602660248201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060448201527f66757475726500000000000000000000000000000000000000000000000000006064820152608401610aef565b6122e96306efa1e04261357c565b8111156123425760405162461bcd60e51b815260206004820152602160248201527f566f74696e67206c6f636b2063616e20626520332e3639207965617273206d616044820152600f60fb1b6064820152608401610aef565b600b60008154612351906137dd565b90915550600b546123628482612d9c565b5060008181526001602081815260409283902083518085019094528054600f0b84528201549083015261239a91839189918691612807565b95945050505050565b600080839050600062093a808083604001516123bf91906135c9565b6123c99190613594565b905060005b60ff81101561247d576123e462093a808361357c565b91506000858311156123f85785925061240c565b50600082815260076020526040902054600f0b5b604084015161241b9084613548565b846020015161242a9190613673565b8451859061243990839061371a565b600f0b9052508286141561244d575061247d565b808460200181815161245f919061377c565b600f0b9052505060408301829052612476816137dd565b90506123ce565b5060008260000151600f0b121561249357600082525b50516001600160801b03169392505050565b6000438211156124b7576124b761351c565b600083815260066020526040812054815b608081101561255b578183106124dd5761255b565b600060026124eb848661357c565b6124f690600161357c565b61250091906135c9565b6000888152600560205260409020909150869082633b9aca00811061252757612527613506565b60030201600201541161253c5780935061254a565b612547600182613548565b92505b50612554816137dd565b90506124c8565b50600085815260056020526040812083633b9aca00811061257e5761257e613506565b604080516080810182526003928302939093018054600f81810b8652600160801b909104900b60208501526001810154918401919091526002015460608301525490915060006125ce878361277d565b600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925080848410156126ad57600060048161263287600161357c565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606080830182905286015191925061268f9190613548565b9250836040015181604001516126a59190613548565b9150506126d1565b60608301516126bc9043613548565b91508260400151426126ce9190613548565b90505b6040830151821561270e578284606001518c6126ed9190613548565b6126f79084613594565b61270191906135c9565b61270b908261357c565b90505b604087015161271d9082613548565b876020015161272c9190613673565b8751889061273b90839061371a565b600f90810b90915288516000910b12905061276b57505093516001600160801b03169650610a4595505050505050565b60009950505050505050505050610a45565b60008082815b60808110156127fd57818310612798576127fd565b600060026127a6848661357c565b6127b190600161357c565b6127bb91906135c9565b60008181526004602052604090206002015490915087106127de578093506127ec565b6127e9600182613548565b92505b506127f6816137dd565b9050612783565b5090949350505050565b6000548290612816868261357c565b6000908155604080518082019091528181526020810191909152825160208085015190830152600f0b815282518790849061285290839061377c565b600f0b905250851561286657602083018690525b6000888152600160208181526040909220855181546fffffffffffffffffffffffffffffffff19166001600160801b03909116178155918501519101556128ae888285611b41565b3387158015906128d0575060048560048111156128cd576128cd6137f8565b14155b1561297a576040516323b872dd60e01b81526001600160a01b038281166004830152306024830152604482018a90527f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303816000875af115801561294a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061296e919061355f565b61297a5761297a61351c565b8360200151816001600160a01b03167fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6248b8b89426040516129be949392919061380e565b60405180910390a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c836129f28a8261357c565b6040805192835260208301919091520160405180910390a1505050505050505050565b60008281526006602052604081205480612a33576000915050610a45565b600084815260056020526040812082633b9aca008110612a5557612a55613506565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b602083015260018101549282018390526002015460608201529150612aa1908561384c565b8160200151612ab09190613673565b81518290612abf90839061371a565b600f90810b90915282516000910b12159050612ada57600081525b51600f0b9150610a459050565b606060405180610120016040528060fd8152602001613c6160fd9139905080612b0f86612eb0565b604051602001612b209291906138a4565b604051602081830303815290604052905080612b3b85612eb0565b604051602001612b4c929190613937565b604051602081830303815290604052905080612b6784612eb0565b604051602001612b789291906139ca565b604051602081830303815290604052905080612b9383612eb0565b604051602001612ba4929190613a5d565b60405160208183030381529060405290506000612bf1612bc387612eb0565b612bcc84612fc6565b604051602001612bdd929190613adf565b604051602081830303815290604052612fc6565b905080604051602001612c049190613c07565b604051602081830303815290604052915050949350505050565b6000818152600c60205260409020546001600160a01b03838116911614612c4757612c4761351c565b6000818152600d60205260409020546001600160a01b031615612c81576000818152600d6020526040902080546001600160a01b03191690555b5050565b6000818152600c60205260409020546001600160a01b03838116911614612cae57612cae61351c565b6000818152600c6020526040902080546001600160a01b0319169055612cd4828261312e565b6001600160a01b0382166000908152600e60205260408120805460019290612cfd908490613548565b90915550505050565b6000818152600c60205260409020546001600160a01b031615612d2b57612d2b61351c565b6000818152600c6020908152604080832080546001600160a01b0319166001600160a01b038716908117909155808452600e8084528285208054600f865284872081885286528487208890558787526010865293862093909355908452909152805460019290612cfd90849061357c565b60006001600160a01b038316612db457612db461351c565b612dbe8383612d06565b823b15612e6f57604051630a85bd0160e11b8152336004820152600060248201819052604482018490526080606483015260848201819052906001600160a01b0385169063150b7a029060a4016020604051808303816000875af1158015612e2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e4e919061360f565b90506001600160e01b03198116630a85bd0160e11b14612e6d57600080fd5b505b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a450600192915050565b606081612ed45750506040805180820190915260018152600360fc1b602082015290565b8160005b8115612efe5780612ee8816137dd565b9150612ef79050600a836135c9565b9150612ed8565b60008167ffffffffffffffff811115612f1957612f196133ac565b6040519080825280601f01601f191660200182016040528015612f43576020820181803683370190505b5090505b8415610e9f57612f58600183613548565b9150612f65600a86613c4c565b612f7090603061357c565b60f81b818381518110612f8557612f85613506565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350612fbf600a866135c9565b9450612f47565b6060815160001415612fe657505060408051602081019091526000815290565b6000604051806060016040528060408152602001613d5e6040913990506000600384516002613015919061357c565b61301f91906135c9565b61302a906004613594565b9050600061303982602061357c565b67ffffffffffffffff811115613051576130516133ac565b6040519080825280601f01601f19166020018201604052801561307b576020820181803683370190505b509050818152600183018586518101602084015b818310156130e95760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b9382019390935260040161308f565b600389510660018114613103576002811461311457613120565b613d3d60f01b600119830152613120565b603d60f81b6000198301525b509398975050505050505050565b6001600160a01b0382166000908152600e602052604081205461315390600190613548565b600083815260106020526040902054909150808214156131a3576001600160a01b0384166000908152600f6020908152604080832085845282528083208390558583526010909152812055611558565b6001600160a01b03939093166000908152600f602090815260408083209383529281528282208054868452848420819055835260109091528282209490945592839055908252812055565b6001600160e01b03198116811461320457600080fd5b50565b60006020828403121561321957600080fd5b8135610d64816131ee565b60005b8381101561323f578181015183820152602001613227565b838111156115585750506000910152565b60008151808452613268816020860160208601613224565b601f01601f19169290920160200192915050565b602081526000610d646020830184613250565b6000602082840312156132a157600080fd5b5035919050565b80356001600160a01b03811681146132bf57600080fd5b919050565b600080604083850312156132d757600080fd5b6132e0836132a8565b946020939093013593505050565b6000806040838503121561330157600080fd5b50508035926020909101359150565b60008060006060848603121561332557600080fd5b61332e846132a8565b925061333c602085016132a8565b9150604084013590509250925092565b60006020828403121561335e57600080fd5b610d64826132a8565b801515811461320457600080fd5b6000806040838503121561338857600080fd5b613391836132a8565b915060208301356133a181613367565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600080600080608085870312156133d857600080fd5b6133e1856132a8565b93506133ef602086016132a8565b925060408501359150606085013567ffffffffffffffff8082111561341357600080fd5b818701915087601f83011261342757600080fd5b813581811115613439576134396133ac565b604051601f8201601f19908116603f01168101908382118183101715613461576134616133ac565b816040528281528a602084870101111561347a57600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b6000806000606084860312156134b357600080fd5b83359250602084013591506134ca604085016132a8565b90509250925092565b600080604083850312156134e657600080fd5b6134ef836132a8565b91506134fd602084016132a8565b90509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008282101561355a5761355a613532565b500390565b60006020828403121561357157600080fd5b8151610d6481613367565b6000821982111561358f5761358f613532565b500190565b60008160001904831182151516156135ae576135ae613532565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826135d8576135d86135b3565b500490565b60006001600160a01b0380871683528086166020840152508360408301526080606083015261104b6080830184613250565b60006020828403121561362157600080fd5b8151610d64816131ee565b600081600f0b83600f0b80613643576136436135b3565b6f7fffffffffffffffffffffffffffffff1982146000198214161561366a5761366a613532565b90059392505050565b600081600f0b83600f0b6f7fffffffffffffffffffffffffffffff6000821360008413838304851182821616156136ac576136ac613532565b6f7fffffffffffffffffffffffffffffff1960008512828116878305871216156136d8576136d8613532565b600087129250858205871284841616156136f4576136f4613532565b8585058712818416161561370a5761370a613532565b5050509290910295945050505050565b600081600f0b83600f0b60008112816f7fffffffffffffffffffffffffffffff190183128115161561374e5761374e613532565b816f7fffffffffffffffffffffffffffffff01831381161561377257613772613532565b5090039392505050565b600081600f0b83600f0b60008212826f7fffffffffffffffffffffffffffffff038213811516156137af576137af613532565b826f7fffffffffffffffffffffffffffffff190382128116156137d4576137d4613532565b50019392505050565b60006000198214156137f1576137f1613532565b5060010190565b634e487b7160e01b600052602160045260246000fd5b84815260208101849052608081016005841061383a57634e487b7160e01b600052602160045260246000fd5b60408201939093526060015292915050565b60008083128015600160ff1b85018412161561386a5761386a613532565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01831381161561389e5761389e613532565b50500390565b600083516138b6818460208801613224565b7f746f6b656e20000000000000000000000000000000000000000000000000000090830190815283516138f0816006840160208801613224565b7f3c2f746578743e3c7465787420783d2231302220793d2234302220636c61737360069290910191820152671e913130b9b2911f60c11b6026820152602e01949350505050565b60008351613949818460208801613224565b7f62616c616e63654f662000000000000000000000000000000000000000000000908301908152835161398381600a840160208801613224565b7f3c2f746578743e3c7465787420783d2231302220793d2236302220636c617373600a9290910191820152671e913130b9b2911f60c11b602a820152603201949350505050565b600083516139dc818460208801613224565b7f6c6f636b65645f656e64200000000000000000000000000000000000000000009083019081528351613a1681600b840160208801613224565b7f3c2f746578743e3c7465787420783d2231302220793d2238302220636c617373600b9290910191820152671e913130b9b2911f60c11b602b820152603301949350505050565b60008351613a6f818460208801613224565b7f76616c75652000000000000000000000000000000000000000000000000000009083019081528351613aa9816006840160208801613224565b7f3c2f746578743e3c2f7376673e0000000000000000000000000000000000000060069290910191820152601301949350505050565b7f7b226e616d65223a20226c6f636b202300000000000000000000000000000000815260008351613b17816010850160208801613224565b7f222c20226465736372697074696f6e223a2022337863616c69627572206c6f636010918401918201527f6b732c2063616e206265207573656420746f20626f6f7374206761756765207960308201527f69656c64732c20766f7465206f6e20746f6b656e20656d697373696f6e2c206160508201527f6e64207265636569766520627269626573222c2022696d616765223a2022646160708201527f74613a696d6167652f7376672b786d6c3b6261736536342c000000000000000060908201528351613bec8160a8840160208801613224565b61227d60f01b60a8929091019182015260aa01949350505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000815260008251613c3f81601d850160208701613224565b91909101601d0192915050565b600082613c5b57613c5b6135b3565b50069056fe3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f73766722207072657365727665417370656374526174696f3d22784d696e594d696e206d656574222076696577426f783d223020302033353020333530223e3c7374796c653e2e62617365207b2066696c6c3a2077686974653b20666f6e742d66616d696c793a2073657269663b20666f6e742d73697a653a20313470783b207d3c2f7374796c653e3c726563742077696474683d223130302522206865696768743d2231303025222066696c6c3d22626c61636b22202f3e3c7465787420783d2231302220793d2232302220636c6173733d2262617365223e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220df3c451f65daf5bb1117644602fdfcd5ffee660600ae7b0ed4e640ca122f642b64736f6c634300080b0033ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef000000000000000000000000d2568accd10a4c98e87c44e9920360031ad89fcb

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103415760003560e01c80638c2c9baf116101bd578063c87b56dd116100f9578063e7e242d4116100a2578063f8a057631161007c578063f8a0576314610867578063fbd3a29d1461088b578063fc0c546a1461089e578063fd4a77f1146108c557600080fd5b8063e7e242d414610805578063e985e9c514610818578063ee99fe281461085457600080fd5b8063d4e54c3b116100d3578063d4e54c3b146107bf578063e0514aba146107d2578063e441135c146107e557600080fd5b8063c87b56dd1461075b578063d1c2babb1461076e578063d1febfb91461078157600080fd5b8063a183af5211610166578063b45a3c0e11610140578063b45a3c0e146106e5578063b88d4fde1461072d578063c1f0fb9f14610740578063c2c4c5c11461075357600080fd5b8063a183af52146106ac578063a22cb465146106bf578063a4d855df146106d257600080fd5b806395d89b411161019757806395d89b411461039f578063981b24d014610686578063986b7d8a1461069957600080fd5b80638c2c9baf146106475780638fbb38ff1461065a578063900cf0cf1461067d57600080fd5b80632f745c591161028c5780634bc2a657116102355780636f5488371161020f5780636f548837146105de57806370a08231146105fe5780637116c60c14610611578063711974841461062457600080fd5b80634bc2a6571461058f5780636352211e146105a257806365fc3873146105cb57600080fd5b8063430c208111610266578063430c208114610543578063461f711c1461055657806346c96aac1461057c57600080fd5b80632f745c59146104e0578063313ce5671461051657806342842e0e1461053057600080fd5b80631376f3da116102ee57806323b872dd116102c857806323b872dd146104b457806325a58b56146104c75780632e1a7d4d146104cd57600080fd5b80631376f3da1461045e57806318160ddd146104995780631c984bc3146104a157600080fd5b8063081812fc1161031f578063081812fc146103e8578063095ea7b3146104295780630d6a20331461043e57600080fd5b806301ffc9a714610346578063047fc9aa1461038857806306fdde031461039f575b600080fd5b610373610354366004613207565b6001600160e01b03191660009081526012602052604090205460ff1690565b60405190151581526020015b60405180910390f35b61039160005481565b60405190815260200161037f565b6103db6040518060400160405280600581526020017f76654e465400000000000000000000000000000000000000000000000000000081525081565b60405161037f919061327c565b6104116103f636600461328f565b6000908152600d60205260409020546001600160a01b031690565b6040516001600160a01b03909116815260200161037f565b61043c6104373660046132c4565b6108d8565b005b61039161044c36600461328f565b60086020526000908152604090205481565b61047161046c3660046132ee565b6109c1565b60408051600f95860b81529390940b602084015292820152606081019190915260800161037f565b610391610a08565b6103916104af3660046132ee565b610a18565b61043c6104c2366004613310565b610a4b565b43610391565b61043c6104db36600461328f565b610a5c565b6103916104ee3660046132c4565b6001600160a01b03919091166000908152600f60209081526040808320938352929052205490565b61051e601281565b60405160ff909116815260200161037f565b61043c61053e366004613310565b610d3d565b6103736105513660046132c4565b610d58565b61056961056436600461328f565b610d6b565b604051600f9190910b815260200161037f565b600a54610411906001600160a01b031681565b61043c61059d36600461334c565b610dae565b6104116105b036600461328f565b6000908152600c60205260409020546001600160a01b031690565b6103916105d93660046132ee565b610de7565b6103916105ec36600461328f565b60026020526000908152604090205481565b61039161060c36600461334c565b610e29565b61039161061f36600461328f565b610e47565b61056961063236600461328f565b600760205260009081526040902054600f0b81565b6103916106553660046132ee565b610ea7565b61037361066836600461328f565b60096020526000908152604090205460ff1681565b61039160035481565b61039161069436600461328f565b610eb3565b61043c6106a736600461328f565b611055565b61043c6106ba3660046132ee565b611099565b61043c6106cd366004613375565b6111db565b61043c6106e03660046132ee565b611260565b6107136106f336600461328f565b60016020819052600091825260409091208054910154600f9190910b9082565b60408051600f9390930b835260208301919091520161037f565b61043c61073b3660046133c2565b611469565b61043c61074e36600461328f565b61155e565b61043c61158d565b6103db61076936600461328f565b6115cd565b61043c61077c3660046132ee565b61167a565b61047161078f36600461328f565b600460205260009081526040902080546001820154600290920154600f82810b93600160801b909304900b919084565b6103916107cd36600461349e565b611809565b6103916107e03660046132ee565b61184c565b6103916107f336600461328f565b60066020526000908152604090205481565b61039161081336600461328f565b611858565b6103736108263660046134d3565b6001600160a01b03918216600090815260116020908152604080832093909416825291909152205460ff1690565b61043c6108623660046132ee565b611880565b61039161087536600461328f565b6000908152600160208190526040909120015490565b61043c61089936600461328f565b611997565b6104117f000000000000000000000000d2568accd10a4c98e87c44e9920360031ad89fcb81565b61043c6108d336600461328f565b6119c8565b6000818152600c60205260409020546001600160a01b0316806108fa57600080fd5b806001600160a01b0316836001600160a01b0316141561091957600080fd5b6000828152600c60209081526040808320546001600160a01b0385811685526011845282852033808752945291909320549216149060ff16818061095a5750805b61096357600080fd5b6000848152600d602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918716917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050505050565b600560205281600052604060002081633b9aca0081106109e057600080fd5b6003020180546001820154600290920154600f82810b9550600160801b90920490910b925084565b6000610a1342610e47565b905090565b600082815260056020526040812082633b9aca008110610a3a57610a3a613506565b600302016001015490505b92915050565b610a57838383336119fa565b505050565b60135460ff16600114610a6e57600080fd5b6013805460ff19166002179055610a853382611adb565b610a9157610a9161351c565b600081815260086020526040902054158015610abc575060008181526009602052604090205460ff16155b610af85760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b60448201526064015b60405180910390fd5b60008181526001602081815260409283902083518085019094528054600f0b845290910154908201819052421015610b725760405162461bcd60e51b815260206004820152601660248201527f546865206c6f636b206469646e277420657870697265000000000000000000006044820152606401610aef565b805160408051808201825260008082526020808301828152878352600191829052938220925183546fffffffffffffffffffffffffffffffff19166001600160801b039091161783559251919092015554600f9190910b90610bd48282613548565b600081905550610bfe848460405180604001604052806000600f0b81526020016000815250611b41565b60405163a9059cbb60e01b8152336004820152602481018390527f000000000000000000000000d2568accd10a4c98e87c44e9920360031ad89fcb6001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610c6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c8f919061355f565b610c9b57610c9b61351c565b610ca484612172565b60408051858152602081018490524281830152905133917f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca94919081900360600190a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c81610d128482613548565b6040805192835260208301919091520160405180910390a150506013805460ff191660011790555050565b610a5783838360405180602001604052806000815250611469565b6000610d648383611adb565b9392505050565b6000818152600660209081526040808320546005909252822081633b9aca008110610d9857610d98613506565b6003020154600160801b9004600f0b9392505050565b600a546001600160a01b03163314610dc557600080fd5b600a80546001600160a01b0319166001600160a01b0392909216919091179055565b60135460009060ff16600114610dfc57600080fd5b6013805460ff19166002179055610e14838333612231565b90506013805460ff1916600117905592915050565b6001600160a01b0381166000908152600e6020526040812054610a45565b600354600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b93810193909352600181015491830191909152600201546060820152909190610e9f81856123a3565b949350505050565b6000610d6483836124a5565b600043821115610ec557610ec561351c565b6003546000610ed4848361277d565b600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925083831015610fe3576000600481610f3786600161357c565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606080830182905285015191925014610fdd5782606001518160600151610fa39190613548565b83604001518260400151610fb79190613548565b6060850151610fc6908a613548565b610fd09190613594565b610fda91906135c9565b91505b50611032565b43826060015114611032576060820151610ffd9043613548565b604083015161100c9042613548565b606084015161101b9089613548565b6110259190613594565b61102f91906135c9565b90505b61104b82828460400151611046919061357c565b6123a3565b9695505050505050565b600a546001600160a01b0316331461106c57600080fd5b60008181526008602052604090205461108790600190613548565b60009182526008602052604090912055565b60135460ff166001146110ab57600080fd5b6013805460ff191660021790556110c23383611adb565b6110ce576110ce61351c565b60008281526001602081815260409283902083518085019094528054600f0b84529091015490820152816111045761110461351c565b60008160000151600f0b1361115b5760405162461bcd60e51b815260206004820152601660248201527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006044820152606401610aef565b428160200151116111ba5760405162461bcd60e51b8152602060048201526024808201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686044820152636472617760e01b6064820152608401610aef565b6111c983836000846002612807565b50506013805460ff1916600117905550565b6001600160a01b0382163314156111f4576111f461351c565b3360008181526011602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b60135460ff1660011461127257600080fd5b6013805460ff191660021790556112893383611adb565b6112955761129561351c565b600082815260016020818152604080842081518083019092528054600f0b825290920154908201529062093a80806112cd854261357c565b6112d791906135c9565b6112e19190613594565b9050428260200151116113365760405162461bcd60e51b815260206004820152600c60248201527f4c6f636b206578706972656400000000000000000000000000000000000000006044820152606401610aef565b60008260000151600f0b1361138d5760405162461bcd60e51b815260206004820152601160248201527f4e6f7468696e67206973206c6f636b65640000000000000000000000000000006044820152606401610aef565b816020015181116113e05760405162461bcd60e51b815260206004820152601f60248201527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006044820152606401610aef565b6113ee6306efa1e04261357c565b8111156114475760405162461bcd60e51b815260206004820152602160248201527f566f74696e67206c6f636b2063616e20626520332e3639207965617273206d616044820152600f60fb1b6064820152608401610aef565b61145684600083856003612807565b50506013805460ff191660011790555050565b611475848484336119fa565b823b1561155857604051630a85bd0160e11b81526000906001600160a01b0385169063150b7a02906114b19033908990889088906004016135dd565b6020604051808303816000875af11580156114d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f4919061360f565b90506001600160e01b03198116630a85bd0160e11b146115565760405162461bcd60e51b815260206004820152601960248201527f4552433732313a207472616e736665722072656a6563746564000000000000006044820152606401610aef565b505b50505050565b600a546001600160a01b0316331461157557600080fd5b6000908152600960205260409020805460ff19169055565b6115cb600060405180604001604052806000600f0b8152602001600081525060405180604001604052806000600f0b81526020016000815250611b41565b565b6000818152600c60205260409020546060906001600160a01b03166116345760405162461bcd60e51b815260206004820152601b60248201527f517565727920666f72206e6f6e6578697374656e7420746f6b656e00000000006044820152606401610aef565b60008281526001602081815260409283902083518085019094528054600f0b84529091015490820152610d648361166b8142612a15565b60208401518451600f0b612ae7565b6000828152600860205260409020541580156116a5575060008281526009602052604090205460ff16155b6116dc5760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b6044820152606401610aef565b808214156116e957600080fd5b6116f33383611adb565b6116fc57600080fd5b6117063382611adb565b61170f57600080fd5b6000828152600160208181526040808420815180830183528154600f90810b82529185015481850190815287875285855283872084518086019095528054840b855290950154938301849052805194519095929490910b92111561177757826020015161177d565b83602001515b604080518082018252600080825260208083018281528b83526001808352858420945185546fffffffffffffffffffffffffffffffff19166001600160801b0390911617855590519301929092558251808401909352808352908201529091506117ea9087908690611b41565b6117f386612172565b611801858383866004612807565b505050505050565b60135460009060ff1660011461181e57600080fd5b6013805460ff19166002179055611836848484612231565b90506013805460ff191660011790559392505050565b6000610d648383612a15565b60008181526002602052604081205443141561187657506000919050565b610a458242612a15565b60135460ff1660011461189257600080fd5b6013805460ff1916600217905560008281526001602081815260409283902083518085019094528054600f0b84529091015490820152816118d257600080fd5b60008160000151600f0b136119295760405162461bcd60e51b815260206004820152601660248201527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006044820152606401610aef565b428160200151116119885760405162461bcd60e51b8152602060048201526024808201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686044820152636472617760e01b6064820152608401610aef565b6111c983836000846000612807565b600a546001600160a01b031633146119ae57600080fd5b60008181526008602052604090205461108790600161357c565b600a546001600160a01b031633146119df57600080fd5b6000908152600960205260409020805460ff19166001179055565b600082815260086020526040902054158015611a25575060008281526009602052604090205460ff16155b611a5c5760405162461bcd60e51b8152602060048201526008602482015267185d1d1858da195960c21b6044820152606401610aef565b611a668183611adb565b611a6f57600080fd5b611a798483612c1e565b611a838483612c85565b611a8d8383612d06565b6000828152600260205260408082204390555183916001600160a01b0380871692908816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a450505050565b6000818152600c6020908152604080832054600d8352818420546001600160a01b039182168086526011855283862088841680885295529285205492938085149392909116149060ff168280611b2e5750815b80611b365750805b979650505050505050565b60408051608081018252600080825260208201819052918101829052606081019190915260408051608081018252600080825260208201819052918101829052606081019190915260035460009081908715611cad57428760200151118015611bb1575060008760000151600f0b135b15611bf6578651611bc7906306efa1e09061362c565b600f0b602080870191909152870151611be1904290613548565b8560200151611bf09190613673565b600f0b85525b428660200151118015611c10575060008660000151600f0b135b15611c55578551611c26906306efa1e09061362c565b600f0b602080860191909152860151611c40904290613548565b8460200151611c4f9190613673565b600f0b84525b602080880151600090815260078252604090205490870151600f9190910b935015611cad57866020015186602001511415611c9257829150611cad565b602080870151600090815260079091526040902054600f0b91505b604080516080810182526000808252602082015242918101919091524360608201528115611d22575060008181526004602090815260409182902082516080810184528154600f81810b8352600160801b909104900b9281019290925260018101549282019290925260029091015460608201525b604081015181600042831015611d6f576040840151611d419042613548565b6060850151611d509043613548565b611d6290670de0b6b3a7640000613594565b611d6c91906135c9565b90505b600062093a80611d7f81866135c9565b611d899190613594565b905060005b60ff811015611f0457611da462093a808361357c565b9150600042831115611db857429250611dcc565b50600082815260076020526040902054600f0b5b611dd68684613548565b8760200151611de59190613673565b87518890611df490839061371a565b600f0b905250602087018051829190611e0e90839061377c565b600f90810b90915288516000910b12159050611e2957600087525b60008760200151600f0b1215611e4157600060208801525b60408088018490528501519295508592670de0b6b3a764000090611e659085613548565b611e6f9086613594565b611e7991906135c9565b8560600151611e88919061357c565b6060880152611e9860018961357c565b975042831415611eae5750436060870152611f04565b6000888152600460209081526040918290208951918a01516001600160801b03908116600160801b029216919091178155908801516001820155606088015160029091015550611efd816137dd565b9050611d8e565b505060038590558b15611f8f5788602001518860200151611f25919061371a565b84602001818151611f36919061377c565b600f0b90525088518851611f4a919061371a565b84518590611f5990839061377c565b600f90810b90915260208601516000910b12159050611f7a57600060208501525b60008460000151600f0b1215611f8f57600084525b6000858152600460209081526040918290208651918701516001600160801b03908116600160801b02921691909117815590850151600182015560608501516002909101558b1561216457428b602001511115612050576020890151611ff5908861377c565b96508a602001518a602001511415612019576020880151612016908861371a565b96505b60208b810151600090815260079091526040902080546fffffffffffffffffffffffffffffffff19166001600160801b0389161790555b428a6020015111156120b4578a602001518a6020015111156120b457602088015161207b908761371a565b60208b810151600090815260079091526040902080546fffffffffffffffffffffffffffffffff19166001600160801b03831617905595505b60008c8152600660205260408120546120ce90600161357c565b905080600660008f815260200190815260200160002081905550428960400181815250504389606001818152505088600560008f815260200190815260200160002082633b9aca00811061212457612124613506565b825160208401516001600160801b03908116600160801b029116176003919091029190910190815560408201516001820155606090910151600290910155505b505050505050505050505050565b61217c3382611adb565b6121c85760405162461bcd60e51b815260206004820181905260248201527f63616c6c6572206973206e6f74206f776e6572206e6f7220617070726f7665646044820152606401610aef565b6000818152600c60205260408120546001600160a01b0316906121eb90836108d8565b6121f58183612c85565b60405182906000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b60008062093a8080612243864261357c565b61224d91906135c9565b6122579190613594565b90506000851161226657600080fd5b4281116122db5760405162461bcd60e51b815260206004820152602660248201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060448201527f66757475726500000000000000000000000000000000000000000000000000006064820152608401610aef565b6122e96306efa1e04261357c565b8111156123425760405162461bcd60e51b815260206004820152602160248201527f566f74696e67206c6f636b2063616e20626520332e3639207965617273206d616044820152600f60fb1b6064820152608401610aef565b600b60008154612351906137dd565b90915550600b546123628482612d9c565b5060008181526001602081815260409283902083518085019094528054600f0b84528201549083015261239a91839189918691612807565b95945050505050565b600080839050600062093a808083604001516123bf91906135c9565b6123c99190613594565b905060005b60ff81101561247d576123e462093a808361357c565b91506000858311156123f85785925061240c565b50600082815260076020526040902054600f0b5b604084015161241b9084613548565b846020015161242a9190613673565b8451859061243990839061371a565b600f0b9052508286141561244d575061247d565b808460200181815161245f919061377c565b600f0b9052505060408301829052612476816137dd565b90506123ce565b5060008260000151600f0b121561249357600082525b50516001600160801b03169392505050565b6000438211156124b7576124b761351c565b600083815260066020526040812054815b608081101561255b578183106124dd5761255b565b600060026124eb848661357c565b6124f690600161357c565b61250091906135c9565b6000888152600560205260409020909150869082633b9aca00811061252757612527613506565b60030201600201541161253c5780935061254a565b612547600182613548565b92505b50612554816137dd565b90506124c8565b50600085815260056020526040812083633b9aca00811061257e5761257e613506565b604080516080810182526003928302939093018054600f81810b8652600160801b909104900b60208501526001810154918401919091526002015460608301525490915060006125ce878361277d565b600081815260046020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925080848410156126ad57600060048161263287600161357c565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606080830182905286015191925061268f9190613548565b9250836040015181604001516126a59190613548565b9150506126d1565b60608301516126bc9043613548565b91508260400151426126ce9190613548565b90505b6040830151821561270e578284606001518c6126ed9190613548565b6126f79084613594565b61270191906135c9565b61270b908261357c565b90505b604087015161271d9082613548565b876020015161272c9190613673565b8751889061273b90839061371a565b600f90810b90915288516000910b12905061276b57505093516001600160801b03169650610a4595505050505050565b60009950505050505050505050610a45565b60008082815b60808110156127fd57818310612798576127fd565b600060026127a6848661357c565b6127b190600161357c565b6127bb91906135c9565b60008181526004602052604090206002015490915087106127de578093506127ec565b6127e9600182613548565b92505b506127f6816137dd565b9050612783565b5090949350505050565b6000548290612816868261357c565b6000908155604080518082019091528181526020810191909152825160208085015190830152600f0b815282518790849061285290839061377c565b600f0b905250851561286657602083018690525b6000888152600160208181526040909220855181546fffffffffffffffffffffffffffffffff19166001600160801b03909116178155918501519101556128ae888285611b41565b3387158015906128d0575060048560048111156128cd576128cd6137f8565b14155b1561297a576040516323b872dd60e01b81526001600160a01b038281166004830152306024830152604482018a90527f000000000000000000000000d2568accd10a4c98e87c44e9920360031ad89fcb16906323b872dd906064016020604051808303816000875af115801561294a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061296e919061355f565b61297a5761297a61351c565b8360200151816001600160a01b03167fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6248b8b89426040516129be949392919061380e565b60405180910390a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c836129f28a8261357c565b6040805192835260208301919091520160405180910390a1505050505050505050565b60008281526006602052604081205480612a33576000915050610a45565b600084815260056020526040812082633b9aca008110612a5557612a55613506565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b602083015260018101549282018390526002015460608201529150612aa1908561384c565b8160200151612ab09190613673565b81518290612abf90839061371a565b600f90810b90915282516000910b12159050612ada57600081525b51600f0b9150610a459050565b606060405180610120016040528060fd8152602001613c6160fd9139905080612b0f86612eb0565b604051602001612b209291906138a4565b604051602081830303815290604052905080612b3b85612eb0565b604051602001612b4c929190613937565b604051602081830303815290604052905080612b6784612eb0565b604051602001612b789291906139ca565b604051602081830303815290604052905080612b9383612eb0565b604051602001612ba4929190613a5d565b60405160208183030381529060405290506000612bf1612bc387612eb0565b612bcc84612fc6565b604051602001612bdd929190613adf565b604051602081830303815290604052612fc6565b905080604051602001612c049190613c07565b604051602081830303815290604052915050949350505050565b6000818152600c60205260409020546001600160a01b03838116911614612c4757612c4761351c565b6000818152600d60205260409020546001600160a01b031615612c81576000818152600d6020526040902080546001600160a01b03191690555b5050565b6000818152600c60205260409020546001600160a01b03838116911614612cae57612cae61351c565b6000818152600c6020526040902080546001600160a01b0319169055612cd4828261312e565b6001600160a01b0382166000908152600e60205260408120805460019290612cfd908490613548565b90915550505050565b6000818152600c60205260409020546001600160a01b031615612d2b57612d2b61351c565b6000818152600c6020908152604080832080546001600160a01b0319166001600160a01b038716908117909155808452600e8084528285208054600f865284872081885286528487208890558787526010865293862093909355908452909152805460019290612cfd90849061357c565b60006001600160a01b038316612db457612db461351c565b612dbe8383612d06565b823b15612e6f57604051630a85bd0160e11b8152336004820152600060248201819052604482018490526080606483015260848201819052906001600160a01b0385169063150b7a029060a4016020604051808303816000875af1158015612e2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e4e919061360f565b90506001600160e01b03198116630a85bd0160e11b14612e6d57600080fd5b505b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a450600192915050565b606081612ed45750506040805180820190915260018152600360fc1b602082015290565b8160005b8115612efe5780612ee8816137dd565b9150612ef79050600a836135c9565b9150612ed8565b60008167ffffffffffffffff811115612f1957612f196133ac565b6040519080825280601f01601f191660200182016040528015612f43576020820181803683370190505b5090505b8415610e9f57612f58600183613548565b9150612f65600a86613c4c565b612f7090603061357c565b60f81b818381518110612f8557612f85613506565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350612fbf600a866135c9565b9450612f47565b6060815160001415612fe657505060408051602081019091526000815290565b6000604051806060016040528060408152602001613d5e6040913990506000600384516002613015919061357c565b61301f91906135c9565b61302a906004613594565b9050600061303982602061357c565b67ffffffffffffffff811115613051576130516133ac565b6040519080825280601f01601f19166020018201604052801561307b576020820181803683370190505b509050818152600183018586518101602084015b818310156130e95760039283018051603f601282901c811687015160f890811b8552600c83901c8216880151811b6001860152600683901c8216880151811b60028601529116860151901b9382019390935260040161308f565b600389510660018114613103576002811461311457613120565b613d3d60f01b600119830152613120565b603d60f81b6000198301525b509398975050505050505050565b6001600160a01b0382166000908152600e602052604081205461315390600190613548565b600083815260106020526040902054909150808214156131a3576001600160a01b0384166000908152600f6020908152604080832085845282528083208390558583526010909152812055611558565b6001600160a01b03939093166000908152600f602090815260408083209383529281528282208054868452848420819055835260109091528282209490945592839055908252812055565b6001600160e01b03198116811461320457600080fd5b50565b60006020828403121561321957600080fd5b8135610d64816131ee565b60005b8381101561323f578181015183820152602001613227565b838111156115585750506000910152565b60008151808452613268816020860160208601613224565b601f01601f19169290920160200192915050565b602081526000610d646020830184613250565b6000602082840312156132a157600080fd5b5035919050565b80356001600160a01b03811681146132bf57600080fd5b919050565b600080604083850312156132d757600080fd5b6132e0836132a8565b946020939093013593505050565b6000806040838503121561330157600080fd5b50508035926020909101359150565b60008060006060848603121561332557600080fd5b61332e846132a8565b925061333c602085016132a8565b9150604084013590509250925092565b60006020828403121561335e57600080fd5b610d64826132a8565b801515811461320457600080fd5b6000806040838503121561338857600080fd5b613391836132a8565b915060208301356133a181613367565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600080600080608085870312156133d857600080fd5b6133e1856132a8565b93506133ef602086016132a8565b925060408501359150606085013567ffffffffffffffff8082111561341357600080fd5b818701915087601f83011261342757600080fd5b813581811115613439576134396133ac565b604051601f8201601f19908116603f01168101908382118183101715613461576134616133ac565b816040528281528a602084870101111561347a57600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b6000806000606084860312156134b357600080fd5b83359250602084013591506134ca604085016132a8565b90509250925092565b600080604083850312156134e657600080fd5b6134ef836132a8565b91506134fd602084016132a8565b90509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008282101561355a5761355a613532565b500390565b60006020828403121561357157600080fd5b8151610d6481613367565b6000821982111561358f5761358f613532565b500190565b60008160001904831182151516156135ae576135ae613532565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826135d8576135d86135b3565b500490565b60006001600160a01b0380871683528086166020840152508360408301526080606083015261104b6080830184613250565b60006020828403121561362157600080fd5b8151610d64816131ee565b600081600f0b83600f0b80613643576136436135b3565b6f7fffffffffffffffffffffffffffffff1982146000198214161561366a5761366a613532565b90059392505050565b600081600f0b83600f0b6f7fffffffffffffffffffffffffffffff6000821360008413838304851182821616156136ac576136ac613532565b6f7fffffffffffffffffffffffffffffff1960008512828116878305871216156136d8576136d8613532565b600087129250858205871284841616156136f4576136f4613532565b8585058712818416161561370a5761370a613532565b5050509290910295945050505050565b600081600f0b83600f0b60008112816f7fffffffffffffffffffffffffffffff190183128115161561374e5761374e613532565b816f7fffffffffffffffffffffffffffffff01831381161561377257613772613532565b5090039392505050565b600081600f0b83600f0b60008212826f7fffffffffffffffffffffffffffffff038213811516156137af576137af613532565b826f7fffffffffffffffffffffffffffffff190382128116156137d4576137d4613532565b50019392505050565b60006000198214156137f1576137f1613532565b5060010190565b634e487b7160e01b600052602160045260246000fd5b84815260208101849052608081016005841061383a57634e487b7160e01b600052602160045260246000fd5b60408201939093526060015292915050565b60008083128015600160ff1b85018412161561386a5761386a613532565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01831381161561389e5761389e613532565b50500390565b600083516138b6818460208801613224565b7f746f6b656e20000000000000000000000000000000000000000000000000000090830190815283516138f0816006840160208801613224565b7f3c2f746578743e3c7465787420783d2231302220793d2234302220636c61737360069290910191820152671e913130b9b2911f60c11b6026820152602e01949350505050565b60008351613949818460208801613224565b7f62616c616e63654f662000000000000000000000000000000000000000000000908301908152835161398381600a840160208801613224565b7f3c2f746578743e3c7465787420783d2231302220793d2236302220636c617373600a9290910191820152671e913130b9b2911f60c11b602a820152603201949350505050565b600083516139dc818460208801613224565b7f6c6f636b65645f656e64200000000000000000000000000000000000000000009083019081528351613a1681600b840160208801613224565b7f3c2f746578743e3c7465787420783d2231302220793d2238302220636c617373600b9290910191820152671e913130b9b2911f60c11b602b820152603301949350505050565b60008351613a6f818460208801613224565b7f76616c75652000000000000000000000000000000000000000000000000000009083019081528351613aa9816006840160208801613224565b7f3c2f746578743e3c2f7376673e0000000000000000000000000000000000000060069290910191820152601301949350505050565b7f7b226e616d65223a20226c6f636b202300000000000000000000000000000000815260008351613b17816010850160208801613224565b7f222c20226465736372697074696f6e223a2022337863616c69627572206c6f636010918401918201527f6b732c2063616e206265207573656420746f20626f6f7374206761756765207960308201527f69656c64732c20766f7465206f6e20746f6b656e20656d697373696f6e2c206160508201527f6e64207265636569766520627269626573222c2022696d616765223a2022646160708201527f74613a696d6167652f7376672b786d6c3b6261736536342c000000000000000060908201528351613bec8160a8840160208801613224565b61227d60f01b60a8929091019182015260aa01949350505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000815260008251613c3f81601d850160208701613224565b91909101601d0192915050565b600082613c5b57613c5b6135b3565b50069056fe3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f73766722207072657365727665417370656374526174696f3d22784d696e594d696e206d656574222076696577426f783d223020302033353020333530223e3c7374796c653e2e62617365207b2066696c6c3a2077686974653b20666f6e742d66616d696c793a2073657269663b20666f6e742d73697a653a20313470783b207d3c2f7374796c653e3c726563742077696474683d223130302522206865696768743d2231303025222066696c6c3d22626c61636b22202f3e3c7465787420783d2231302220793d2232302220636c6173733d2262617365223e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220df3c451f65daf5bb1117644602fdfcd5ffee660600ae7b0ed4e640ca122f642b64736f6c634300080b0033

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

000000000000000000000000d2568accd10a4c98e87c44e9920360031ad89fcb

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

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


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