Sonic Blaze Testnet

Rings veUSD (veUSD)

Overview

TokenID

4

Total Transfers

-

Market

Onchain Market Cap

-

Circulating Supply Market Cap

-
TokenID: 4
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.24+commit.e11b9ed9

Optimization Enabled:
Yes with 100000 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 9 : VotingEscrow.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

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

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

    struct LockedBalance {
        int128 amount;
        uint256 end;
    }

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

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

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

    event Deposit(
        address indexed provider,
        uint256 tokenId,
        uint256 value,
        uint256 indexed locktime,
        DepositType deposit_type,
        uint256 ts
    );
    event Withdraw(address indexed provider, uint256 tokenId, uint256 value, uint256 ts);
    event Supply(uint256 prevSupply, uint256 supply);
    event VotingApproval(address indexed owner, address indexed operator, uint256 indexed tokenId);
    event VotingApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/


    error NotToken();
    error AlreadyAttached();
    error NotApprovedOrOwner();
    error NoLock();
    error LockExpired();
    error LockInFuture();
    error LockTooLong();
    error TooManyDelegates();
    error InvalidSignature();
    error InvalidNonce();
    error SignatureExpired();

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

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

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

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

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

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

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

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

    /// @notice Contract constructor
    /// @param token_addr `THENA` token address
    constructor(address token_addr, address art_proxy) {
        token = token_addr;
        team = msg.sender;
        artProxy = art_proxy;

        point_history[0].blk = block.number;
        point_history[0].ts = block.timestamp;

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

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

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

    /// @dev reentrancy guard
    uint8 internal constant _not_entered = 1;
    uint8 internal constant _entered = 2;
    uint8 internal _entered_state = 1;

    modifier nonreentrant() {
        require(_entered_state == _not_entered);
        _entered_state = _entered;
        _;
        _entered_state = _not_entered;
    }

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

    string public constant name = "Rings veUSD";
    string public constant symbol = "veUSD";
    string public constant version = "1.0.0";
    uint8 public constant decimals = 18;

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

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

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

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

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

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

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

    /// @dev Returns the number of NFTs owned by `_owner`.
    ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.
    /// @param _owner Address for whom to query the balance.
    function _balance(address _owner) internal view returns (uint256) {
        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 (uint256) {
        return _balance(_owner);
    }

    /*//////////////////////////////////////////////////////////////
                         VOTING APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @dev Mapping from NFT ID to voting approved address.
    mapping(uint256 => address) internal idToVotingApprovals;

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

    /// @dev Get the voting approved address for a single NFT.
    /// @param _tokenId ID of the NFT to query the voting approval of.
    function getVotingApproved(uint256 _tokenId) external view returns (address) {
        return idToVotingApprovals[_tokenId];
    }

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

    /// @dev Returns whether the given voter can vote a given token ID
    /// @param _voter address of the voter 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 _isVotingApprovedOrOwner(address _voter, uint256 _tokenId) internal view returns (bool) {
        address owner = idToOwner[_tokenId];
        bool voterIsOwner = owner == _voter;
        bool voterIsApproved = idToVotingApprovals[_tokenId] == _voter;
        bool voterIsApprovedForAll = (ownerToVotingOperators[owner])[_voter];
        bool isApproved = idToApprovals[_tokenId] == _voter;
        bool _isApprovedForAll = (ownerToOperators[owner])[_voter];

        return voterIsOwner || voterIsApproved || voterIsApprovedForAll || isApproved || _isApprovedForAll;
    }

    function isVotingApprovedOrOwner(address _voter, uint256 _tokenId) external view returns (bool) {
        return _isVotingApprovedOrOwner(_voter, _tokenId);
    }

    /*//////////////////////////////////////////////////////////////
                        VOTING APPROVAL LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @dev Set or reaffirm the voting approved address for an NFT. The zero address indicates there is no voting
    /// approved address.
    ///      Throws unless `msg.sender` is the current NFT owner, or an authorized voting 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 approveVoting(address _approved, uint256 _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
        idToVotingApprovals[_tokenId] = _approved;
        emit VotingApproval(owner, _approved, _tokenId);
    }

    /// @dev Enables or disables voting approval for a third party ("operator") to manage all of
    ///      `msg.sender`'s assets votes. It also emits the VotingApprovalForAll 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 voting operators.
    /// @param _approved True if the voting operators is approved, false to revoke approval.
    function setVotingApprovalForAll(address _operator, bool _approved) external {
        // Throws if `_operator` is the `msg.sender`
        assert(_operator != msg.sender);
        ownerToVotingOperators[msg.sender][_operator] = _approved;
        emit VotingApprovalForAll(msg.sender, _operator, _approved);
    }

    /* TRANSFER FUNCTIONS */
    /// @dev Clear an approval of a given address. Caller should check beforehand if the sender is the owner.
    function _clearVotingApproval(uint256 _tokenId) internal {
        if (idToVotingApprovals[_tokenId] != address(0)) {
            // Reset approvals
            idToVotingApprovals[_tokenId] = address(0);
        }
    }

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

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

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

    mapping(uint256 => uint256) public ownership_change;

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

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

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

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

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

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

    /// @dev Returns whether the given spender can transfer a given token ID
    /// @param _spender address of the spender to query
    /// @param _tokenId uint ID of the token to be transferred
    /// @return bool whether the msg.sender is approved for the given token ID, is an operator of the owner, or is the
    /// owner of the token
    function _isApprovedOrOwner(address _spender, uint256 _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, uint256 _tokenId) external view returns (bool) {
        return _isApprovedOrOwner(_spender, _tokenId);
    }

    /// @dev Exeute transfer of a NFT.
    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the approved
    ///      address for this NFT. (NOTE: `msg.sender` not allowed in internal function so pass `_sender`.)
    ///      Throws if `_to` is the zero address.
    ///      Throws if `_from` is not the current owner.
    ///      Throws if `_tokenId` is not a valid NFT.
    function _transferFrom(address _from, address _to, uint256 _tokenId, address _sender) internal {
        if (attachments[_tokenId] != 0 || voted[_tokenId]) {
            revert AlreadyAttached();
        }
        // Check requirements
        require(_isApprovedOrOwner(_sender, _tokenId));
        // Clear approval. Throws if `_from` is not the current owner
        _clearApproval(_from, _tokenId);
        // Clear voting approval.
        _clearVotingApproval(_tokenId);
        // Remove NFT. Throws if `_tokenId` is not a valid NFT
        _removeTokenFrom(_from, _tokenId);
        // auto re-delegate
        _moveTokenDelegates(delegates(_from), delegates(_to), _tokenId);
        // Add NFT
        _addTokenTo(_to, _tokenId);
        // Set the block of ownership transfer (for Flash NFT protection)
        ownership_change[_tokenId] = block.number;
        // Log the transfer
        emit Transfer(_from, _to, _tokenId);
    }

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

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

    function _isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.
        uint256 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, uint256 _tokenId, bytes memory _data) public {
        _transferFrom(_from, _to, _tokenId, msg.sender);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    function _burn(uint256 _tokenId) internal {
        if (!_isApprovedOrOwner(msg.sender, _tokenId)) {
            revert NotApprovedOrOwner();
        }

        address owner = ownerOf(_tokenId);

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

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

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

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

    uint256 internal constant WEEK = 1 weeks;
    uint256 internal constant MAXTIME = 52 weeks;
    int128 internal constant iMAXTIME = 52 weeks;
    uint256 internal constant MULTIPLIER = 1 ether;

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

    /// @notice Get the most recently recorded rate of voting power decrease for `_tokenId`
    /// @param _tokenId token of the NFT
    /// @return Value of the slope
    function get_last_user_slope(uint256 _tokenId) external view returns (int128) {
        uint256 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(uint256 _tokenId, uint256 _idx) external view returns (uint256) {
        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(uint256 _tokenId) external view returns (uint256) {
        return locked[_tokenId].end;
    }

    /// @notice Record global and per-user data to checkpoint
    /// @param _tokenId NFT token ID. No user checkpoint if 0
    /// @param old_locked Pevious locked amount / end lock time for the user
    /// @param new_locked New locked amount / end lock time for the user
    function _checkpoint(uint256 _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;
        uint256 _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];
        }
        uint256 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;
        uint256 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
        {
            uint256 t_i = (last_checkpoint / WEEK) * WEEK;
            for (uint256 i = 0; i < 255; ++i) {
                // Hopefully it won't happen that this won't get used in 5 years!
                // If it does, users will be able to withdraw but vote weight will be broken
                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
            uint256 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(
        uint256 _tokenId,
        uint256 _value,
        uint256 unlock_time,
        LockedBalance memory locked_balance,
        DepositType deposit_type
    ) internal {
        LockedBalance memory _locked = locked_balance;
        uint256 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 && deposit_type != DepositType.SPLIT_TYPE) {
            assert(IERC20(token).transferFrom(from, address(this), _value));
        }

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

    function block_number() external view returns (uint256) {
        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(uint256 _tokenId, uint256 _value) external nonreentrant {
        LockedBalance memory _locked = locked[_tokenId];

        require(_value > 0); // dev: need non-zero value
        if (_locked.amount == 0) {
            revert NoLock();
        }
        if (_locked.end <= block.timestamp) {
            revert LockExpired();
        }
        _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(uint256 _value, uint256 _lock_duration, address _to) internal returns (uint256) {
        uint256 unlock_time = (block.timestamp + _lock_duration) / WEEK * WEEK; // Locktime is rounded down to weeks

        require(_value > 0); // dev: need non-zero value
        if (unlock_time <= block.timestamp) {
            revert LockInFuture();
        }
        if (unlock_time > block.timestamp + MAXTIME) {
            revert LockTooLong();
        }

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

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

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

    /// @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(uint256 _value, uint256 _lock_duration, address _to)
        external
        nonreentrant
        returns (uint256)
    {
        return _create_lock(_value, _lock_duration, _to);
    }

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

        LockedBalance memory _locked = locked[_tokenId];

        assert(_value > 0); // dev: need non-zero value
        if (_locked.amount == 0) {
            revert NoLock();
        }
        if (_locked.end <= block.timestamp) {
            revert LockExpired();
        }

        _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(uint256 _tokenId, uint256 _lock_duration) external nonreentrant {
        assert(_isApprovedOrOwner(msg.sender, _tokenId));

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

        if (_locked.end <= block.timestamp) {
            revert LockExpired();
        }
        if (_locked.amount == 0) {
            revert NoLock();
        }
        if (unlock_time <= _locked.end) {
            revert LockInFuture();
        }
        if (unlock_time > block.timestamp + MAXTIME) {
            revert LockTooLong();
        }

        _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(uint256 _tokenId) external nonreentrant {
        assert(_isApprovedOrOwner(msg.sender, _tokenId));
        if (attachments[_tokenId] != 0 || voted[_tokenId]) {
            revert AlreadyAttached();
        }

        LockedBalance memory _locked = locked[_tokenId];
        if (block.timestamp < _locked.end) {
            revert LockExpired();
        }
        uint256 value = uint256(int256(_locked.amount));

        locked[_tokenId] = LockedBalance(0, 0);
        uint256 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);
    }

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

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

    /// @notice Binary search to estimate timestamp for block number
    /// @param _block Block to find
    /// @param max_epoch Don't go beyond this epoch
    /// @return Approximate timestamp for block
    function _find_block_epoch(uint256 _block, uint256 max_epoch) internal view returns (uint256) {
        // Binary search
        uint256 _min = 0;
        uint256 _max = max_epoch;
        for (uint256 i = 0; i < 128; ++i) {
            // Will be always enough for 128-bit numbers
            if (_min >= _max) {
                break;
            }
            uint256 _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(uint256 _tokenId, uint256 _t) internal view returns (uint256) {
        uint256 _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 uint256(int256(last_point.bias));
        }
    }

    function balanceOfNFT(uint256 _tokenId) external view returns (uint256) {
        if (ownership_change[_tokenId] == block.number) return 0;
        return _balanceOfNFT(_tokenId, block.timestamp);
    }

    function balanceOfNFTAt(uint256 _tokenId, uint256 _t) external view returns (uint256) {
        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(uint256 _tokenId, uint256 _block) internal view returns (uint256) {
        // Copying and pasting totalSupply code because Vyper cannot pass by
        // reference yet
        assert(_block <= block.number);

        // Binary search
        uint256 _min = 0;
        uint256 _max = user_point_epoch[_tokenId];
        for (uint256 i = 0; i < 128; ++i) {
            // Will be always enough for 128-bit numbers
            if (_min >= _max) {
                break;
            }
            uint256 _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];

        uint256 max_epoch = epoch;
        uint256 _epoch = _find_block_epoch(_block, max_epoch);
        Point memory point_0 = point_history[_epoch];
        uint256 d_block = 0;
        uint256 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;
        }
        uint256 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 uint256(uint128(upoint.bias));
        } else {
            return 0;
        }
    }

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

    /// @notice Calculate total voting power at some point in the past
    /// @param _block Block to calculate the total voting power at
    /// @return Total voting power at `_block`
    function totalSupplyAt(uint256 _block) external view returns (uint256) {
        assert(_block <= block.number);
        uint256 _epoch = epoch;
        uint256 target_epoch = _find_block_epoch(_block, _epoch);

        Point memory point = point_history[target_epoch];
        uint256 dt = 0;
        if (target_epoch < _epoch) {
            Point memory point_next = point_history[target_epoch + 1];
            if (point.blk != point_next.blk) {
                dt = ((_block - point.blk) * (point_next.ts - point.ts)) / (point_next.blk - point.blk);
            }
        } else {
            if (point.blk != block.number) {
                dt = ((_block - point.blk) * (block.timestamp - point.ts)) / (block.number - point.blk);
            }
        }
        // Now dt contains info on how far are we beyond point
        return _supply_at(point, point.ts + dt);
    }
    /// @notice Calculate total voting power at some point in the past
    /// @param point The point (bias/slope) to start search from
    /// @param t Time to calculate the total voting power at
    /// @return Total voting power at that time

    function _supply_at(Point memory point, uint256 t) internal view returns (uint256) {
        Point memory last_point = point;
        uint256 t_i = (last_point.ts / WEEK) * WEEK;
        for (uint256 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 uint256(uint128(last_point.bias));
    }

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

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

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

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

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

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

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

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

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

    function merge(uint256 _from, uint256 _to) external {
        if (attachments[_from] != 0 || voted[_from]) {
            revert AlreadyAttached();
        }
        require(_from != _to);
        require(_isApprovedOrOwner(msg.sender, _from));
        require(_isApprovedOrOwner(msg.sender, _to));

        LockedBalance memory _locked0 = locked[_from];
        LockedBalance memory _locked1 = locked[_to];
        uint256 value0 = uint256(int256(_locked0.amount));
        uint256 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);
    }

    /**
     * @notice split NFT into multiple
     * @param amounts   % of split
     * @param _tokenId  NFTs ID
     */
    function split(uint256[] memory amounts, uint256 _tokenId) external {
        // check permission and vote
        if (attachments[_tokenId] != 0 || voted[_tokenId]) {
            revert AlreadyAttached();
        }
        require(_isApprovedOrOwner(msg.sender, _tokenId));

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

        // reset supply, _deposit_for increase it
        supply = supply - value;

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

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

        // save end
        uint256 unlock_time = end;
        if (unlock_time <= block.timestamp) {
            revert LockInFuture();
        }
        if (unlock_time > block.timestamp + MAXTIME) {
            revert LockTooLong();
        }

        // mint
        uint256 _value = 0;
        for (i = 0; i < length; ++i) {
            ++tokenId;
            _tokenId = tokenId;
            _mint(_to, _tokenId);
            _value = value * amounts[i] / totalWeight;
            _deposit_for(_tokenId, _value, unlock_time, locked[_tokenId], DepositType.SPLIT_TYPE);
        }
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                numCheckpoints[srcRep] = srcRepNum + 1;
            }

            if (dstRep != address(0)) {
                uint32 dstRepNum = numCheckpoints[dstRep];
                uint256[] storage dstRepOld =
                    dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].tokenIds : checkpoints[dstRep][0].tokenIds;
                uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep);
                uint256[] storage dstRepNew = checkpoints[dstRep][nextDstRepNum].tokenIds;
                // All the same plus _tokenId
                if (dstRepOld.length + 1 > MAX_DELEGATES) {
                    revert TooManyDelegates();
                }
                uint256 length = dstRepOld.length;
                for (uint256 i = 0; i < length; ++i) {
                    uint256 tId = dstRepOld[i];
                    dstRepNew.push(tId);
                }
                dstRepNew.push(_tokenId);

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

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

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

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

                numCheckpoints[srcRep] = srcRepNum + 1;
            }

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

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

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

        _delegates[delegator] = delegatee;

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

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

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

        bytes32 domainSeparator = keccak256(
            abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), keccak256(bytes(version)), block.chainid, address(this))
        );
        bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        if (signatory == address(0)) {
            revert InvalidSignature();
        }
        if (nonce != nonces[signatory]++) {
            revert InvalidNonce();
        }
        if (block.timestamp > expiry) {
            revert SignatureExpired();
        }
        return _delegate(signatory, delegatee);
    }
}

File 2 of 9 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.20;

import {IERC721} from "../IERC721.sol";

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

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

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

File 3 of 9 : IVotes.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.20;

/**
 * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
 */
interface IVotes {
    /**
     * @dev The signature used has expired.
     */
    error VotesExpiredSignature(uint256 expiry);

    /**
     * @dev Emitted when an account changes their delegate.
     */
    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);

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

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

    /**
     * @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
     * configured to use block numbers, this will return the value at the end of the corresponding block.
     */
    function getPastVotes(address account, uint256 timepoint) external view returns (uint256);

    /**
     * @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
     * configured to use block numbers, this will return the value at the end of the corresponding block.
     *
     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
     * Votes that have not been delegated are still part of total supply, even though they would not participate in a
     * vote.
     */
    function getPastTotalSupply(uint256 timepoint) external view returns (uint256);

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

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

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

File 4 of 9 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

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

File 5 of 9 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}

File 6 of 9 : IVeArtProxy.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

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

File 7 of 9 : IVotingEscrow.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

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

    struct LockedBalance {
        int128 amount;
        uint256 end;
    }

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

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

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

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

    function isVotingApprovedOrOwner(address, uint256) external view returns (bool);
    function delegateVotingControl(address, uint256) external;

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

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

    function balanceOfNFT(uint256 _id) external view returns (uint256);
    function balanceOf(address _owner) external view returns (uint256);
    function totalSupply() external view returns (uint256);
    function supply() external view returns (uint256);

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

File 8 of 9 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

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

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

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

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

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

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

File 9 of 9 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @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
{
  "remappings": [
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "solmate/=lib/solmate/src/",
    "solady/=lib/solady/src/",
    "src/=src/",
    "test/=test/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 100000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": true,
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"token_addr","type":"address"},{"internalType":"address","name":"art_proxy","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyAttached","type":"error"},{"inputs":[],"name":"InvalidNonce","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"LockExpired","type":"error"},{"inputs":[],"name":"LockInFuture","type":"error"},{"inputs":[],"name":"LockTooLong","type":"error"},{"inputs":[],"name":"NoLock","type":"error"},{"inputs":[],"name":"NotApprovedOrOwner","type":"error"},{"inputs":[],"name":"NotToken","type":"error"},{"inputs":[],"name":"SignatureExpired","type":"error"},{"inputs":[],"name":"TooManyDelegates","type":"error"},{"inputs":[{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"VotesExpiredSignature","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousVotes","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newVotes","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"locktime","type":"uint256"},{"indexed":false,"internalType":"enum VotingEscrow.DepositType","name":"deposit_type","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"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":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"VotingApproval","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":"VotingApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DELEGATES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"abstain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approveVoting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"artProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"attach","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"attachments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"balanceOfAtNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"balanceOfNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_t","type":"uint256"}],"name":"balanceOfNFTAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"block_number","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"checkpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"create_lock","outputs":[{"internalType":"uint256","name":"","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":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"deposit_for","outputs":[],"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":"timestamp","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPastVotesIndex","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getVotingApproved","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":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isVotingApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_voter","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isVotingApprovedOrOwner","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":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ownership_change","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"point_history","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_proxy","type":"address"}],"name":"setArtProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_team","type":"address"}],"name":"setTeam","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_voter","type":"address"}],"name":"setVoter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setVotingApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slope_changes","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"split","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"team","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_tokenIndex","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"t","type":"uint256"}],"name":"totalSupplyAtT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"user_point_epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"user_point_history","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"user_point_history__ts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"voted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"voting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a034620001625762005f7f601f38829003908101601f19168301906001600160401b0382118483101762000167578084916040948594855283398101031262000162576200005c602062000054846200017d565b93016200017d565b9160019260ff19918483600654161760065560805260018060a01b031990338286541617855560018060a01b0316906002541617600255600092838052600360205282842043600282015581429101556301ffc9a760e01b8452600460205282842081838254161790556380ac58cd60e01b84528284208183825416179055635b5e139f60e01b8452828420918254161790556005549051917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef823083838180a430908280a4615dec9081620001938239608051818181610eb501528181612c29015281816146ea015281816149f001528181614b7001528181614ce001526152380152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001625756fe6080604052600436101561001257600080fd5b60003560e01c806301ffc9a7146104c2578063047fc9aa146104bd57806306fdde03146104b85780630758c7d8146104b3578063081812fc146104ae578063095cf5c6146104a9578063095ea7b3146104a45780630d6a20331461049f5780631376f3da1461049a57806316b7a0111461049557806318160ddd146104905780631c984bc31461048b57806320606b701461048657806323b872dd1461048157806325a58b561461047c5780632e1a7d4d146104775780632e720f7d146104725780632f745c591461046d578063313ce567146104685780633a46b1a81461046357806342842e0e1461045e578063430c208114610459578063461f711c1461045457806346c96aac1461044f5780634bc2a6571461044a57806354fd4d50146104455780635594a0451461044057806356afe7441461043b578063587cde1e146104365780635c19a95c146104315780635f5b0c321461042c5780636352211e1461042757806365fc3873146104225780636f5488371461041d5780636fcfff451461041857806370a08231146104135780637116c60c146103fa578063711974841461040e5780637ecebe001461040957806385f2aef2146104045780638c2c9baf146103ff5780638e539e8c146103fa5780638fbb38ff146103f5578063900cf0cf146103f057806395d89b41146103eb578063981b24d0146103e6578063986b7d8a146103e15780639ab24eb0146103dc578063a183af52146103d7578063a22cb465146103d2578063a4d855df146103cd578063b29b8830146103c8578063b45a3c0e146103c3578063b88d4fde146103be578063c1f0fb9f146103b9578063c2c4c5c1146103b4578063c3cda520146103af578063c87b56dd146103aa578063d1c2babb146103a5578063d1febfb9146103a0578063d2aef4571461039b578063d4e54c3b14610396578063e0514aba14610391578063e441135c1461038c578063e7a324dc14610387578063e7e242d414610382578063e9237e121461037d578063e985e9c514610378578063ee99fe2814610373578063ef39933d1461036e578063f1127ed814610369578063f8a0576314610364578063fbd3a29d1461035f578063fc0c546a1461035a5763fd4a77f11461035557600080fd5b612c4d565b612bfc565b612b9f565b612b70565b612aff565b612a54565b6129b4565b612944565b612902565b6128e4565b6128a9565b61287d565b61285c565b6127c5565b612712565b6126b1565b612579565b612417565b612138565b611e95565b611e2a565b611da1565b611d69565b611ced565b611bd4565b611af1565b6119f2565b6119cf565b61194f565b611801565b6117a3565b611785565b611754565b61166b565b611733565b6116ff565b6116b8565b611689565b611624565b6115d7565b6115ab565b61153d565b6114fb565b6114de565b611497565b611456565b6113c7565b611393565b611377565b6112d5565b6112a1565b61125c565b61122b565b6111f1565b611136565b61111a565b6110c5565b61105c565b610d5e565b610d43565b610d28565b610cab565b610c70565b610c4c565b610ac8565b610a5a565b6109e3565b610880565b610817565b6107d5565b6107a0565b610726565b610552565b6104f6565b7fffffffff000000000000000000000000000000000000000000000000000000008116036104f157565b600080fd5b346104f15760206003193601126104f1577fffffffff00000000000000000000000000000000000000000000000000000000600435610534816104c7565b166000526004602052602060ff604060002054166040519015158152f35b346104f15760006003193601126104f1576020601554604051908152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff8211176105bb57604052565b610570565b6080810190811067ffffffffffffffff8211176105bb57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176105bb57604052565b6040519061062a8261059f565b565b6040519061062a826105c0565b67ffffffffffffffff81116105bb57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b604051906106808261059f565b600b82527f52696e67732076655553440000000000000000000000000000000000000000006020830152565b60005b8381106106bf5750506000910152565b81810151838201526020016106af565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361070b815180928187528780880191016106ac565b0116010190565b9060206107239281815201906106cf565b90565b346104f15760006003193601126104f157610756610742610673565b6040519182916020835260208301906106cf565b0390f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104f157565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036104f157565b346104f15760406003193601126104f15760206107c76107be61075a565b60243590612d31565b63ffffffff60405191168152f35b346104f15760206003193601126104f157600435600052600b602052602073ffffffffffffffffffffffffffffffffffffffff60406000205416604051908152f35b346104f15760206003193601126104f15761083061075a565b6001549073ffffffffffffffffffffffffffffffffffffffff80831633036104f1577fffffffffffffffffffffffff00000000000000000000000000000000000000009116911617600155600080f35b346104f15760406003193601126104f15761089961075a565b602435600091818352600760205273ffffffffffffffffffffffffffffffffffffffff806040852054169081156109df57808316928284146109db576109586109ac926108f0876000526007602052604060002090565b5416331460ff610949336109248873ffffffffffffffffffffffffffffffffffffffff16600052600c602052604060002090565b9073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b541681156109d3575b50612ee3565b61096c85600052600b602052604060002090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258480a480f35b905038610952565b8580fd5b8480fd5b346104f15760206003193601126104f15760043560005260166020526020604060002054604051908152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90633b9aca00811015610a55576003020190600090565b610a0f565b346104f15760406003193601126104f1576024356004356000526011602052604060002090633b9aca008110156104f157610a9491610a3e565b508054600182015460029092015460408051600f84810b8252608094851d900b602082015290810193909352606083015290f35b346104f15760406003193601126104f157610756610ae461075a565b60243580600052600760205273ffffffffffffffffffffffffffffffffffffffff806040600020541690831691610bf9610b9f84841495600960205261092486610b63610b4a604060002073ffffffffffffffffffffffffffffffffffffffff90541690565b73ffffffffffffffffffffffffffffffffffffffff1690565b1496610bd1610b4a610bb7610ba6610b9f876109248d73ffffffffffffffffffffffffffffffffffffffff16600052600a602052604060002090565b5460ff1690565b98600052600b602052604060002090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b149573ffffffffffffffffffffffffffffffffffffffff16600052600c602052604060002090565b928415610c44575b508315610c3c575b508215610c34575b508115610c2c575b5060405190151581529081906020820190565b905038610c19565b915038610c11565b925038610c09565b935038610c01565b346104f15760006003193601126104f1576020610c68426132ed565b604051908152f35b346104f15760406003193601126104f157600435600052601160205260206001610ca06024356040600020610a3e565b500154604051908152f35b346104f15760006003193601126104f15760206040517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a8668152f35b60031960609101126104f15773ffffffffffffffffffffffffffffffffffffffff9060043582811681036104f1579160243590811681036104f1579060443590565b346104f157610d41610d3936610ce6565b9033926137c6565b005b346104f15760006003193601126104f1576020604051438152f35b346104f1576020806003193601126104f15760043590600654600160ff8216036104f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655610dbc610db783336138fc565b612eea565b610dd0826000526016602052604060002090565b541580159061103f575b61101557610dfa610df5836000526012602052604060002090565b612f20565b818101514210610feb57610e18610e128251600f0b90565b600f0b90565b92610e2161061d565b93610e496000958681528686820152610e44846000526012602052604060002090565b612f41565b610e7960155493610e62610e5d8487612fc1565b601555565b610e6a61061d565b90878252878783015284613b35565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815233600482015260248101829052918483604481897f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1948515610fe6577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c95610f7594610f2b928992610fb9575b5050612eea565b610f34816140d3565b6040805191825260208201839052429082015233907f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca9490606090a282612fc1565b60408051928352602083019190915290a1610fb660017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b80f35b610fd89250803d10610fdf575b610fd081836105dc565b810190612fce565b3880610f24565b503d610fc6565b612fe3565b60046040517ff6fafba0000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3ca9d617000000000000000000000000000000000000000000000000000000008152fd5b50611057610b9f836000526017602052604060002090565b610dda565b346104f15760206003193601126104f15761107561075a565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104f157167fffffffffffffffffffffffff00000000000000000000000000000000000000006002541617600255600080f35b346104f15760406003193601126104f15773ffffffffffffffffffffffffffffffffffffffff6110f361075a565b16600052600e60205260406000206024356000526020526020604060002054604051908152f35b346104f15760006003193601126104f157602060405160128152f35b346104f15760406003193601126104f15761114f61075a565b6024359073ffffffffffffffffffffffffffffffffffffffff6111728383612d31565b91166000526019602052600161119d819260406000209063ffffffff16600052602052604060002090565b019160009183546000925b8184106111ba57604051858152602090f35b909192936111d8826111cc8789612fef565b90549060031b1c61424a565b81018091116111ec579383019291906111a8565b612cb8565b346104f1576111ff36610ce6565b60405191602083019383851067ffffffffffffffff8611176105bb57610d41946040526000845261344b565b346104f15760406003193601126104f157602061125261124961075a565b602435906138fc565b6040519015158152f35b346104f15760206003193601126104f15760043560005260106020526020611291604060002054601183526040600020610a3e565b505460801d60405190600f0b8152f35b346104f15760006003193601126104f157602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b346104f15760206003193601126104f1576112ee61075a565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104f157167fffffffffffffffffffffffff00000000000000000000000000000000000000006000541617600055600080f35b6040519061134b8261059f565b600582527f312e302e300000000000000000000000000000000000000000000000000000006020830152565b346104f15760006003193601126104f15761075661074261133e565b346104f15760006003193601126104f157602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b346104f15760406003193601126104f15760043567ffffffffffffffff8082116104f157366023830112156104f15781600401359081116105bb578060051b916020926040519261141b60208301856105dc565b835260246020840191830101913683116104f157602401905b82821061144757610d4160243585613100565b81358152908401908401611434565b346104f15760206003193601126104f157602061147961147461075a565b61327d565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346104f15760206003193601126104f1576114b061075a565b73ffffffffffffffffffffffffffffffffffffffff8116156114d7575b610d419033614d6e565b50336114cd565b346104f15760006003193601126104f15760206040516104008152f35b346104f15760206003193601126104f1576004356000526007602052602073ffffffffffffffffffffffffffffffffffffffff60406000205416604051908152f35b346104f15760406003193601126104f157600654600160ff8216036104f15760209060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0080921617600655600161159a3360243560043561513e565b916006541617600655604051908152f35b346104f15760206003193601126104f157600435600052600d6020526020604060002054604051908152f35b346104f15760206003193601126104f15773ffffffffffffffffffffffffffffffffffffffff61160561075a565b16600052601a602052602063ffffffff60406000205416604051908152f35b346104f15760206003193601126104f15773ffffffffffffffffffffffffffffffffffffffff61165261075a565b1660005260086020526020604060002054604051908152f35b346104f15760206003193601126104f1576020610c686004356132ed565b346104f15760206003193601126104f15760043560005260146020526020604060002054600f0b604051908152f35b346104f15760206003193601126104f15773ffffffffffffffffffffffffffffffffffffffff6116e661075a565b16600052601b6020526020604060002054604051908152f35b346104f15760006003193601126104f157602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346104f15760406003193601126104f1576020610c6860243560043561541c565b346104f15760206003193601126104f1576004356000526017602052602060ff604060002054166040519015158152f35b346104f15760006003193601126104f1576020601354604051908152f35b346104f15760006003193601126104f1576107566040516117c38161059f565b600581527f766555534400000000000000000000000000000000000000000000000000000060208201526040519182916020835260208301906106cf565b346104f15760206003193601126104f1576107566118a960043561182743821115612eea565b6118a360135461183781846155f0565b9261185461184f856000526003602052604060002090565b6132b6565b936000928110156119035761184f61186e61187e92613007565b6000526003602052604060002090565b606085019081516060820193845182036118b9575b50505050505b6040830151613036565b9061530c565b6040519081529081906020820190565b6118f99550916118e360406118d56118e9946118f39796612fc1565b92015160408a015190612fc1565b906130b4565b9251905190612fc1565b906130c7565b3880808080611893565b50606084019081519043820361191c575b505050611899565b61194793506119316118f39261193f92612fc1565b6118e3604088015142612fc1565b915143612fc1565b388080611914565b346104f15760206003193601126104f15760043560009073ffffffffffffffffffffffffffffffffffffffff82541633036119cb5780825260166020526040822054907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116111ec5782526016602052604082205580f35b5080fd5b346104f15760206003193601126104f1576020610c686119ed61075a565b61330e565b346104f15760406003193601126104f157600435602435600654600160ff8216036104f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655611a4c610db783336138fc565b816000526012602052611a626040600020612f20565b90611a6e811515612eea565b8151600f0b15611abd576020820151421015610feb57611a8d9261458a565b610d4160017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b60046040517fba112c93000000000000000000000000000000000000000000000000000000008152fd5b801515036104f157565b346104f15760406003193601126104f157611b0a61075a565b602435611b1681611ae7565b611ba481611b7473ffffffffffffffffffffffffffffffffffffffff851694611b4133871415612eea565b33600052600c60205260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405190151581527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b346104f15760406003193601126104f157600435600654600160ff8216036104f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655611c2b610db782336138fc565b611c42610df5826000526012602052604060002090565b611c62611c5d611c5460243542613036565b62093a80900490565b613084565b602082014281511115610feb578251600f0b15611abd5751811115611cc357611c8a42613015565b8111611c9957611a8d92614798565b60046040517fd6946a43000000000000000000000000000000000000000000000000000000008152fd5b60046040517fd5bb0fd9000000000000000000000000000000000000000000000000000000008152fd5b346104f15760406003193601126104f157602060ff611d5d611d0d61075a565b73ffffffffffffffffffffffffffffffffffffffff611d2a61077d565b9116600052600a845260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b54166040519015158152f35b346104f15760206003193601126104f157600435600052601260205260408060002060018154600f0b91015482519182526020820152f35b346104f15760806003193601126104f157611dba61075a565b611dc261077d565b6064359167ffffffffffffffff83116104f157366023840112156104f157826004013591611def83610639565b92611dfd60405194856105dc565b80845236602482870101116104f1576020816000926024610d41980183880137850101526044359161344b565b346104f15760206003193601126104f157600073ffffffffffffffffffffffffffffffffffffffff8154163303611e925760043581526017602052604081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905580f35b80fd5b346104f157600080600319360112611e925760405190611eb48261059f565b80825280602080930152806020604051611ecd8161059f565b8281520152611eda61398d565b50611ee361398d565b5060135491611ef061062c565b908282528260208301524260408301526060914360608201529184612119575b604083019182519285928442116120ed575b969390611f3662093a808395949504613084565b9787985b60ff8a10611f5a575b88610fb689611f558a61186e81601355565b613a57565b611f648991613026565b93888783964281116000146120ac5750610e1296611fc2611fbb611fd894611fb6611fae611fd196611fa8429e8f925b86019b611fa28d51600f0b90565b93612fc1565b906139b2565b9151600f0b90565b6139c9565b600f0b8d52565b8351600f0b613a10565b613a10565b600f0b8252565b81611fe7610e128b51600f0b90565b126120a4575b81611ffc610e128351600f0b90565b1261209c575b505082948383526120468289019761203f895161203961202b6120258a80612fc1565b8b6130b4565b670de0b6b3a7640000900490565b90613036565b8952613007565b9942850361206d5750505050505050610fb69293611f559143905290849338808080611f43565b6001919293949596975061208f89611f558d6000526003602052604060002090565b0198959291909493611f3a565b528738612002565b818952611fed565b9650611fd190611fc2611fbb611fd894611fb6611fae8c611fa8610e126120e76120e0846000526014602052604060002090565b54600f0b90565b98611f94565b9250612113612108612103606088015143612fc1565b61309c565b6118f3835142612fc1565b92611f22565b915061213261184f856000526003602052604060002090565b91611f10565b346104f15760c06003193601126104f15761215161075a565b602435906064359060443560ff831683036104f15773ffffffffffffffffffffffffffffffffffffffff9061219582841661218e33821415612ee3565b1515612ee3565b61219d610673565b948551602080970120956123306122136122fb6123076121bb61133e565b805190860120604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818901908152602081019e909e528d8201929092524660608e01523060808e01529b9093849060a0830190565b03936122457fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018352826105dc565b51902092898c516122b8816122ac8c8c8c8401968760609194939273ffffffffffffffffffffffffffffffffffffffff60808301967fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf845216602083015260408201520152565b038481018352826105dc565b5190208c51938491888301968790916042927f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201520190565b039081018352826105dc565b519020885190815260ff9097166020880152608435604088015260a43560608801526080870190565b866000978892838052039060015afa15610fe6578451928316156123ee576123788373ffffffffffffffffffffffffffffffffffffffff16600052601b602052604060002090565b9081549161238583613057565b9055036123c557421161239c5790610fb691614d6e565b600484517f0819bdcd000000000000000000000000000000000000000000000000000000008152fd5b600485517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b600486517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b346104f15760206003193601126104f15760043573ffffffffffffffffffffffffffffffffffffffff612457610bb7836000526007602052604060002090565b161561254f576000612476610df5836000526012602052604060002090565b61249b610b4a610b4a60025473ffffffffffffffffffffffffffffffffffffffff1690565b6124a5428561424a565b6124b8610e1260208501519451600f0b90565b94612509604051968795869485947fdd9ec149000000000000000000000000000000000000000000000000000000008652600486019094939260609260808301968352602083015260408201520152565b03915afa8015610fe6576107569160009161252c575b5060405191829182610712565b61254991503d806000833e61254181836105dc565b810190613651565b3861251f565b60046040517f21f2b69c000000000000000000000000000000000000000000000000000000008152fd5b346104f15760406003193601126104f1576004356024356125a4826000526016602052604060002090565b5415801590612694575b61101557816125c282610d41941415612ee3565b6125d46125cf82336138fc565b612ee3565b6125e16125cf83336138fc565b6125f8610df5826000526012602052604060002090565b90612610610df5846000526012602052604060002090565b92612687612622610e128551600f0b90565b92612682602086015160208801518082101560001461268c5750955b61266861264961061d565b6000815260006020820152610e44856000526012602052604060002090565b61267061061d565b90600082526000602083015283613b35565b6140d3565b6148ad565b90509561263e565b506126ac610b9f836000526017602052604060002090565b6125ae565b346104f15760206003193601126104f157600435600052600360205260406000208054610756600260018401549301546040519383859460801d90600f0b85909493926060926080830196600f0b8352600f0b602083015260408201520152565b346104f15760406003193601126104f15761272b61075a565b60243561273781611ae7565b61279581611b7473ffffffffffffffffffffffffffffffffffffffff85169461276233871415612eea565b33600052600a60205260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b60405190151581527fccb38c6086037bf3d363115fc480b43f15aeb617ec6296b6a8ab6c70a1560adc60203392a3005b346104f15760606003193601126104f15760443573ffffffffffffffffffffffffffffffffffffffff811681036104f157600654600160ff8216036104f15760016128426107569360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008095161760065560243560043561513e565b916006541617600655604051918291829190602083019252565b346104f15760406003193601126104f1576020610c6860243560043561424a565b346104f15760206003193601126104f15760043560005260106020526020604060002054604051908152f35b346104f15760006003193601126104f15760206040517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf8152f35b346104f15760206003193601126104f1576020610c686004356136b0565b346104f15760206003193601126104f1576004356000526009602052602073ffffffffffffffffffffffffffffffffffffffff60406000205416604051908152f35b346104f15760406003193601126104f157602060ff611d5d61296461075a565b73ffffffffffffffffffffffffffffffffffffffff61298161077d565b9116600052600c845260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b346104f15760406003193601126104f157600435602435600654600160ff8216036104f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00600291161760065581600052601260205260406000209060405191612a1e8361059f565b60018154600f0b9182855201549060208401918252612a3e831515612ee3565b15611abd5751421015610feb57611a8d92614a86565b346104f15760406003193601126104f157612a6d61075a565b602435600091818352600760205273ffffffffffffffffffffffffffffffffffffffff806040852054169081156109df57808316928284146109db57612ac4612ad8926108f0876000526007602052604060002090565b61096c856000526009602052604060002090565b7fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad8480a480f35b346104f15760406003193601126104f157612b1861075a565b6024359063ffffffff821682036104f15760209173ffffffffffffffffffffffffffffffffffffffff612b6792166000526019835260406000209063ffffffff16600052602052604060002090565b54604051908152f35b346104f15760206003193601126104f15760043560005260126020526020600160406000200154604051908152f35b346104f15760206003193601126104f15760043560009073ffffffffffffffffffffffffffffffffffffffff82541633036119cb578082526016602052604082205490600182018092116111ec5782526016602052604082205580f35b346104f15760006003193601126104f157602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346104f15760206003193601126104f157600073ffffffffffffffffffffffffffffffffffffffff8154163303611e9257600435815260176020526040812060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905580f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff63ffffffff809316019182116111ec57565b63ffffffff91821690821603919082116111ec57565b90612d69612d5f8373ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b5463ffffffff1690565b9163ffffffff80841615612ece5782612dc2612da58473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b612dae87612ce7565b63ffffffff16600052602052604060002090565b541115612ed75782612e06612df78473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b60008052602052604060002090565b5411612ece579190612e19600094612ce7565b83851684821611612e2b575050505090565b612e4d612e47612e3b8784612d1b565b60011c637fffffff1690565b82612d1b565b94612e9186612e7c8573ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b9063ffffffff16600052602052604060002090565b54848103612ea25750505050505090565b9380959692939410600014612ebc5750935b929190612e19565b949150612ec890612ce7565b90612eb4565b50505050600090565b50505061072390612ce7565b156104f157565b15612ef157565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b90604051612f2d8161059f565b6020600182948054600f0b84520154910152565b906020600191612f8d8151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b0151910155565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116111ec57565b919082039182116111ec57565b908160209103126104f1575161072381611ae7565b6040513d6000823e3d90fd5b8054821015610a555760005260206000200190600090565b90600182018092116111ec57565b906301dfe20082018092116111ec57565b9062093a8082018092116111ec57565b919082018092116111ec57565b8051821015610a555760209160051b010190565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146111ec5760010190565b9062093a80918281029281840414901517156111ec57565b90670de0b6b3a764000091808302928304036111ec57565b818102929181159184041417156111ec57565b81156130d1570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b613114826000526016602052604060002090565b5415801590613260575b6110155761312f6125cf83336138fc565b613146610bb7836000526007602052604060002090565b9061315e610df5846000526012602052604060002090565b602081015191613172610e128351600f0b90565b9061317e821515612ee3565b61318d610e5d83601554612fc1565b805195600093845b8881106132415750906126826131b09261266861264961061d565b42841115611cc3576131c142613015565b8411611c995760005b8681106131da5750505050505050565b60019061323b60056131f46131ef8254613057565b600555565b546131ff818a6142fd565b508761321e88613219613212878a613043565b518a6130b4565b6130c7565b613235610df5846000526012602052604060002090565b92614bfe565b016131ca565b946132596001916132528887613043565b5190613036565b9501613195565b50613278610b9f836000526017602052604060002090565b61311e565b73ffffffffffffffffffffffffffffffffffffffff80821660005260186020526040600020541680156000146132b1575090565b905090565b906040516132c3816105c0565b606060028294805480600f0b855260801d600f0b6020850152600181015460408501520154910152565b61072390601354600052600360205261330960406000206132b6565b61530c565b73ffffffffffffffffffffffffffffffffffffffff8116600052601a60205263ffffffff604060002054169081156133c35761337961336f60019273ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b612dae8394612ce7565b0160009181546000925b818410613391575050505090565b909192936133af6133a28684612fef565b9054429160031b1c61424a565b81018091116111ec57938301929190613383565b5050600090565b908160209103126104f15751610723816104c7565b9092610723949360809373ffffffffffffffffffffffffffffffffffffffff8092168452166020830152604082015281606082015201906106cf565b3d15613446573d9061342c82610639565b9161343a60405193846105dc565b82523d6000602084013e565b606090565b9291909161345b338385876137c6565b823b613468575b50505050565b6134be92602092600073ffffffffffffffffffffffffffffffffffffffff6040518097819682957f150b7a02000000000000000000000000000000000000000000000000000000009b8c855233600486016133df565b0393165af160009181613620575b5061356b576134d961341b565b80519081613566576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608490fd5b602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000160361359b5738808080613462565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4552433732313a2045524337323152656365697665722072656a65637465642060448201527f746f6b656e7300000000000000000000000000000000000000000000000000006064820152608490fd5b61364391925060203d60201161364a575b61363b81836105dc565b8101906133ca565b90386134cc565b503d613631565b6020818303126104f15780519067ffffffffffffffff82116104f1570181601f820112156104f157805161368481610639565b9261369260405194856105dc565b818452602082840101116104f15761072391602080850191016106ac565b80600052600d60205260406000205443146136d05761072390429061424a565b50600090565b6000818152600760205273ffffffffffffffffffffffffffffffffffffffff806040832054169081156137c257613717846000526007602052604060002090565b5416331460ff61374b336109248573ffffffffffffffffffffffffffffffffffffffff16600052600c602052604060002090565b541681156137ba575b50156119cb5761376e836000526009602052604060002090565b7fffffffffffffffffffffffff000000000000000000000000000000000000000081541690557fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad8280a4565b905038613754565b8280fd5b9092919260009284845260166020526040842054158015906138de575b61101557846137f1916138fc565b156137c2577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90848452600760205273ffffffffffffffffffffffffffffffffffffffff9061388d8661387e8460408920541696613853868216809914612eea565b828952600b6020526040892080548781166138b3575b50506138748361566e565b61147483826156c2565b6138878461327d565b90615a4a565b6138978682615ca9565b436138ac87600052600d602052604060002090565b55169280a4565b7fffffffffffffffffffffffff00000000000000000000000000000000000000001690553880613869565b5060ff6138f5866000526017602052604060002090565b54166137e3565b90600052600760205273ffffffffffffffffffffffffffffffffffffffff9060ff61397083604060002054169284811680851495600b602052604060002054161493600052600c60205260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b5416908215613985575b5081156132b1575090565b91503861397a565b6040519061399a826105c0565b60006060838281528260208201528260408201520152565b90600f0b90600f0b029081600f0b9182036111ec57565b90600f0b90600f0b03906f7fffffffffffffffffffffffffffffff82137fffffffffffffffffffffffffffffffff800000000000000000000000000000008312176111ec57565b90600f0b90600f0b01907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff8313176111ec57565b906060600291613aa38151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b60208101516fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000086549260801b169116178455604081015160018501550151910155565b9190613b065761062a91613a57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b613b3d61398d565b90613b4661398d565b926000806013549284151580613f95575b613b5f61062c565b94600086526020956000878201524260408201526060904360608201529082613f76575b60408201805191600091834211613f4f575b8a90613baa62093a8086989695949504613084565b966000975b60ff8910613e15575b5050509050613bec94508291508c8c87613bd4611f5596601355565b613d6c575b5050506000526003602052604060002090565b613bfb575b5050505050505050565b613c8597613c7f9685808094019542875111613d14575b5050500191825190428211613c92575b5050505050613c7a613c47613c41836000526010602052604060002090565b54613007565b9182613c5d826000526010602052604060002090565b554260408601524360608601526000526011602052604060002090565b610a3e565b90613af7565b3880808080808080613bf1565b5110613ca0575b8080613c22565b613cbd613ccf91613cb7613d0c95890151600f0b90565b906139c9565b91516000526014602052604060002090565b907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b388080613c99565b613d27613d2d92613d4e940151600f0b90565b90613a10565b83830151865114613d56575b613ccf86516000526014602052604060002090565b388281613c12565b613d6790613cb7858c0151600f0b90565b613d39565b613dbc613dc691613cb7611fae85613db5613dae613d9f613d938b613dcd9b0151600f0b90565b878c0151600f0b613cb7565b998d0199611fcc8b51600f0b90565b600f0b8952565b51600f0b90565b8651600f0b613a10565b600f0b8552565b6000613ddd610e128351600f0b90565b12613e0b575b506000613df4610e128551600f0b90565b12613e02575b888c8c613bd9565b60008352613dfa565b6000905238613de3565b613e1e90613026565b60009042811115613f1f5750611fd1613e6991611fc2613e62613e5842985b8d0196611fa8610e12613e518a51600f0b90565b928c612fc1565b8c51600f0b6139c9565b600f0b8b52565b6000613e79610e128951600f0b90565b12613f16575b6000613e8f610e128351600f0b90565b12613f0c575b5081938282528c613eb88289019761203f895161203961202b6120258a80612fc1565b98428503613edd5750505050505050613bec92611f5591439052903889818080613bb8565b60019192939495969750613eff89611f558c6000526003602052604060002090565b0197959092919493613baf565b6000905238613e95565b60008752613e7f565b93613e699150611fd190611fc2613e62613e58613f496120e08a6000526014602052604060002090565b93613e3d565b9150613f70613f65612103606087015143612fc1565b6118f3845142612fc1565b91613b95565b9050613f8f61184f836000526003602052604060002090565b90613b83565b6020820180514210806140bd575b614084575b8989602082019642885111908161406d575b5061401d575b5050613fda6120e082516000526014602052604060002090565b94519081613fea575b5050613b57565b51919350908103614000575082915b3880613fe3565b6120e0614017916000526014602052604060002090565b91613ff9565b61405161405f91613db5602061404761403a6140669751600f0b90565b6301dfe20090600f0b0590565b600f0b9201918252565b611fa8610e12428a51612fc1565b600f0b8a52565b8989613fc0565b905061407d610e128451600f0b90565b1338613fba565b6140b8613dae6140aa61409b61403a8751600f0b90565b600f0b60208c01908152613db5565b611fa8610e12428651612fc1565b613fa8565b50846140cd610e128551600f0b90565b13613fa3565b6140dd81336138fc565b15614220576000818152600760205273ffffffffffffffffffffffffffffffffffffffff806040832054169081156137c2573390614125856000526007602052604060002090565b54161460ff614158336109248573ffffffffffffffffffffffffffffffffffffffff16600052600c602052604060002090565b54168115614218575b50156119cb576141a661417e84600052600b602052604060002090565b7fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b8282827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258280a46141d6836136d6565b6141e8836141e38361327d565b6158fc565b6141f283826156c2565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4565b905038614161565b60046040517fe433766c000000000000000000000000000000000000000000000000000000008152fd5b600081815260106020526040812054909181614267575050905090565b61428891613c7a614282926000526011602052604060002090565b506132b6565b6020810151916040820151938185820395128186128116918613901516176111ec576142d86142d16142c7610e129561072397600f0b90600f0b6139b2565b8451600f0b6139c9565b600f0b8352565b806142e48351600f0b90565b600f0b126142f5575b5051600f0b90565b8152386142ed565b73ffffffffffffffffffffffffffffffffffffffff908282821692614323841515612eea565b61432c8361327d565b90811615158080614568575b614373575b505061434891615ca9565b60007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4600190565b61437e575b8061433d565b90506143ad612d5f8273ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b63ffffffff8116156145335760016143f16143e88473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b612dae84612ce7565b015b60018061442c61440286615d7e565b612e7c8773ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b019180549061040061443d83613007565b11614509579088959493929160005b8281106144d257505050509161449c6144756144cc93614470876143489997615890565b6158e7565b9173ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b9063ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000825416179055565b91614378565b8394959697506144fb6144f56144eb8385969795612fef565b90549060031b1c90565b87615890565b01908996959493929161444c565b60046040517f8c390496000000000000000000000000000000000000000000000000000000008152fd5b6001614562612df78473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b016143f3565b50821515614338565b6040519061457e8261059f565b60006020838281520152565b91906015549161459d610e5d8385613036565b6146006145a8614571565b916145b48151600f0b90565b926145cd6020830194855160208401528290600f0b9052565b6145e26142d186600f0b611fcc8551600f0b90565b6145fa82610e44896000526012602052604060002090565b86613b35565b81151580614790575b80614788575b6146a3575b5160408051948552602085018390526002908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361468a92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081015b0390a382613036565b604080519283526020830191909152819081015b0390a1565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390529390919060208560648160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1928315610fe65761475f61468a947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c97600091614769575b50612eea565b9192509350614614565b614782915060203d602011610fdf57610fd081836105dc565b38614759565b50600161460f565b506001614609565b601554906147a4614571565b93805190602081019182516020880152600f0b86528051600f0b907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff8313176111ec577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9661469e9661484b938352806148a5575b5061484582610e44876000526012602052604060002090565b84613b35565b519060405190815260006020820152600360408201524260608201527fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62460803392a360408051828152602081019290925290918291820190565b84523861482c565b9291614924601554936148c3610e5d8587613036565b6148cb614571565b8151600f0b936148e96020840195865160208501528390600f0b9052565b6149056148fe87600f0b611fcc8651600f0b90565b600f0b8452565b80614a7e575b506145fa82610e44896000526012602052604060002090565b81151580614a76575b80614a6e575b6149a9575b5160408051948552602085018390526004908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361468a92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de624908060808101614681565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390529390919060208560648160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1928315610fe657614a6461468a947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c976000916147695750612eea565b9192509350614938565b506001614933565b50600061492d565b84523861490b565b919060155491614a99610e5d8385613036565b614aa46145a8614571565b81151580614bf6575b80614bee575b614b29575b5160408051948552602085018390526000908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361468a92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de624908060808101614681565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390529390919060208560648160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1928315610fe657614be461468a947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c976000916147695750612eea565b9192509350614ab8565b506001614ab3565b506001614aad565b9291614c14601554936148c3610e5d8587613036565b81151580614d66575b80614d5e575b614c99575b5160408051948552602085018390526005908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361468a92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de624908060808101614681565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390529390919060208560648160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1928315610fe657614d5461468a947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c976000916147695750612eea565b9192509350614c28565b506000614c23565b506001614c1d565b9190614d798361327d565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260186020526040812080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168685161790559290848316928281168481847f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f8980a4848103614e0c575b505050505050509050565b614fe5575b505050614e23575b8080808080614e01565b614e50612d5f8373ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b9063ffffffff821615614fb0576001614e95614e8c8573ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b612dae85612ce7565b015b6001916001614ed2614ea887615d7e565b612e7c8873ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b0190614efe8873ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b5492805490610400614f108684613036565b116145095785835b838110614f9857505050505b828110614f495750505050614f4192935061447561449c916158e7565b803880614e19565b80614f92614f8b8693614f7c8c73ffffffffffffffffffffffffffffffffffffffff16600052600e602052604060002090565b90600052602052604060002090565b5484615890565b01614f24565b614fa86144f56144eb8386612fef565b018690614f18565b6001614fdf612df78573ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b01614e97565b615012612d5f8473ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b9163ffffffff83161561510857600161505761504e8673ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b612dae86612ce7565b01905b600192600161509561506b88615d7e565b612e7c8973ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b0191835493895b8581106150c257505050505050509061449c6144756150ba936158e7565b388080614e11565b806150d16144eb899385612fef565b84866150ea610bb7846000526007602052604060002090565b16036150f8575b500161509c565b6151029087615890565b386150f1565b6001615137612df78673ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b019061505a565b9042018042116111ec5762093a806151569104613084565b81156104f15742811115611cc35761516d42613015565b8111611c995761518c615181600554613057565b8094816005556142fd565b506151a4610df5846000526012602052604060002090565b906151f4601554926151b9610e5d8686613036565b6151c1614571565b8151600f0b936151df6020840195865160208501528390600f0b9052565b6149056148fe88600f0b611fcc8651600f0b90565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018490529260208460648160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1918215610fe6576152ac6152f8937f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c966000916147695750612eea565b516040517fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62433918061468142878c84606091949392608082019582526020820152600160408201520152565b60408051928352602083019190915290a190565b9060408201615322611c5d825162093a80900490565b906000915b60ff8310615378575b5050505061535a61535a82600061534e610e126107239651600f0b90565b1261536f5751600f0b90565b6fffffffffffffffffffffffffffffffff1690565b60008152613db5565b61538190613026565b60009290848111156153fe575083925b60208601906153c96153c26153b86153aa8551600f0b90565b611fa8610e1289518b612fc1565b8951600f0b6139c9565b600f0b8852565b8585146153f757816153e66153ed92611fcc6001969551600f0b90565b600f0b9052565b8383520191615327565b5050615330565b92506154176120e0846000526014602052604060002090565b615391565b61542843831115612eea565b60009061543f816000526010602052604060002090565b546000905b60808210615580575b5050611fd161550d6154776142826155179695613c7a613db5966000526011602052604060002090565b936013549061548682826155f0565b61549d61184f826000526003602052604060002090565b9281101561555c5761184f61186e6154b492613007565b906154db60406154cd6060850151606087015190612fc1565b930151604085015190612fc1565b905b60408401519383615538575b50505050611fa8610e126155016020880151600f0b90565b92604088015190612fc1565b8351600f0b6139c9565b6000600f82900b126136d0576fffffffffffffffffffffffffffffffff1690565b612039926118e3615553969593606061321994015190612fc1565b388080806154e9565b5061556b606083015143612fc1565b9061557a604084015142612fc1565b906154dd565b9092818110156155ea576155a561559f61559a8484613036565b613007565b60011c90565b908560026155c184613c7a886000526011602052604060002090565b500154116155d65750600190935b0190615444565b9391506155e4600191612f94565b916155cf565b9261544d565b60009182915b60808310615605575b50505090565b90919282811015615668578281018082116111ec5760018082018092116111ec571c9082600261563f846000526003602052604060002090565b0154116156545750600190935b0191906155f6565b939250615662600191612f94565b9261564c565b926155ff565b60005260096020526040600020805473ffffffffffffffffffffffffffffffffffffffff811661569c575050565b7fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6000828152600760205273ffffffffffffffffffffffffffffffffffffffff6156f681604084205416918416809214612eea565b838252600760205261572d604083207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b8152600860205260408120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81019081116111ec57838261577c6157ee96600052600f602052604060002090565b548381036157fc57506157b66157c793614f7c8773ffffffffffffffffffffffffffffffffffffffff16600052600e602052604060002090565b55600052600f602052604060002090565b5573ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b6157f88154612f94565b9055565b836157b6916158656158356157c797614f7c8b73ffffffffffffffffffffffffffffffffffffffff16600052600e602052604060002090565b54806157b684614f7c8d73ffffffffffffffffffffffffffffffffffffffff16600052600e602052604060002090565b55614f7c8773ffffffffffffffffffffffffffffffffffffffff16600052600e602052604060002090565b8054680100000000000000008110156105bb576158b291600182018155612fef565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff829392549160031b92831b921b1916179055565b90600163ffffffff809316019182116111ec57565b90919073ffffffffffffffffffffffffffffffffffffffff811615158080615a41575b61592a575b50509050565b615935575b80615924565b615962612d5f8273ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b63ffffffff811615615a0b57600161599d6143e88473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b01935b60019060016159b161440286615d7e565b0186549160005b8381106159dd5750505050506159d692935061447561449c916158e7565b803861592f565b806159ec6144eb87938c612fef565b8381036159fb575b50016159b8565b615a059085615890565b386159f4565b6001615a3a612df78473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b01936159a0565b5083151561591f565b91909173ffffffffffffffffffffffffffffffffffffffff908184169181168281141580615ca0575b615a7f575b5050505050565b615b99575b50615a92575b808080615a78565b615abf612d5f8373ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b9063ffffffff821615615b64576001615afb614e8c8573ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b015b600180615b0c614ea887615d7e565b0191805490610400615b1d83613007565b116145095760005b828110615b4c5750505050615b45939261447061449c9361447593615890565b3880615a8a565b80615b5e6144f56144eb879486612fef565b01615b25565b6001615b93612df78573ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b01615afd565b615bc6612d5f8273ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b63ffffffff811615615c6b576001615c016143e88473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b015b6001906001615c1461440286615d7e565b0181549160005b838110615c3d5750505050509061449c614475615c37936158e7565b38615a84565b80615c4c6144eb879385612fef565b8a8103615c5b575b5001615c1b565b615c659085615890565b38615c54565b6001615c9a612df78473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b01615c03565b50831515615a73565b615d7491600081815260209060078252600f73ffffffffffffffffffffffffffffffffffffffff92604093615ce381868620541615612eea565b8584526007825284842080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff891617905586168352600881528383205494600e825284842086855282528085852055835252205573ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b6157f88154613007565b73ffffffffffffffffffffffffffffffffffffffff16600052601a60205263ffffffff6040600020541680151580615dbf575b156107235761072390612ce7565b5060196020526040600020615dd84291612dae84612ce7565b5414615db156fea164736f6c6343000818000a0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba000000000000000000000000d03a0fd36f58edec885143a03fc546bc58d1794f

Deployed Bytecode

0x6080604052600436101561001257600080fd5b60003560e01c806301ffc9a7146104c2578063047fc9aa146104bd57806306fdde03146104b85780630758c7d8146104b3578063081812fc146104ae578063095cf5c6146104a9578063095ea7b3146104a45780630d6a20331461049f5780631376f3da1461049a57806316b7a0111461049557806318160ddd146104905780631c984bc31461048b57806320606b701461048657806323b872dd1461048157806325a58b561461047c5780632e1a7d4d146104775780632e720f7d146104725780632f745c591461046d578063313ce567146104685780633a46b1a81461046357806342842e0e1461045e578063430c208114610459578063461f711c1461045457806346c96aac1461044f5780634bc2a6571461044a57806354fd4d50146104455780635594a0451461044057806356afe7441461043b578063587cde1e146104365780635c19a95c146104315780635f5b0c321461042c5780636352211e1461042757806365fc3873146104225780636f5488371461041d5780636fcfff451461041857806370a08231146104135780637116c60c146103fa578063711974841461040e5780637ecebe001461040957806385f2aef2146104045780638c2c9baf146103ff5780638e539e8c146103fa5780638fbb38ff146103f5578063900cf0cf146103f057806395d89b41146103eb578063981b24d0146103e6578063986b7d8a146103e15780639ab24eb0146103dc578063a183af52146103d7578063a22cb465146103d2578063a4d855df146103cd578063b29b8830146103c8578063b45a3c0e146103c3578063b88d4fde146103be578063c1f0fb9f146103b9578063c2c4c5c1146103b4578063c3cda520146103af578063c87b56dd146103aa578063d1c2babb146103a5578063d1febfb9146103a0578063d2aef4571461039b578063d4e54c3b14610396578063e0514aba14610391578063e441135c1461038c578063e7a324dc14610387578063e7e242d414610382578063e9237e121461037d578063e985e9c514610378578063ee99fe2814610373578063ef39933d1461036e578063f1127ed814610369578063f8a0576314610364578063fbd3a29d1461035f578063fc0c546a1461035a5763fd4a77f11461035557600080fd5b612c4d565b612bfc565b612b9f565b612b70565b612aff565b612a54565b6129b4565b612944565b612902565b6128e4565b6128a9565b61287d565b61285c565b6127c5565b612712565b6126b1565b612579565b612417565b612138565b611e95565b611e2a565b611da1565b611d69565b611ced565b611bd4565b611af1565b6119f2565b6119cf565b61194f565b611801565b6117a3565b611785565b611754565b61166b565b611733565b6116ff565b6116b8565b611689565b611624565b6115d7565b6115ab565b61153d565b6114fb565b6114de565b611497565b611456565b6113c7565b611393565b611377565b6112d5565b6112a1565b61125c565b61122b565b6111f1565b611136565b61111a565b6110c5565b61105c565b610d5e565b610d43565b610d28565b610cab565b610c70565b610c4c565b610ac8565b610a5a565b6109e3565b610880565b610817565b6107d5565b6107a0565b610726565b610552565b6104f6565b7fffffffff000000000000000000000000000000000000000000000000000000008116036104f157565b600080fd5b346104f15760206003193601126104f1577fffffffff00000000000000000000000000000000000000000000000000000000600435610534816104c7565b166000526004602052602060ff604060002054166040519015158152f35b346104f15760006003193601126104f1576020601554604051908152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff8211176105bb57604052565b610570565b6080810190811067ffffffffffffffff8211176105bb57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176105bb57604052565b6040519061062a8261059f565b565b6040519061062a826105c0565b67ffffffffffffffff81116105bb57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b604051906106808261059f565b600b82527f52696e67732076655553440000000000000000000000000000000000000000006020830152565b60005b8381106106bf5750506000910152565b81810151838201526020016106af565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361070b815180928187528780880191016106ac565b0116010190565b9060206107239281815201906106cf565b90565b346104f15760006003193601126104f157610756610742610673565b6040519182916020835260208301906106cf565b0390f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104f157565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036104f157565b346104f15760406003193601126104f15760206107c76107be61075a565b60243590612d31565b63ffffffff60405191168152f35b346104f15760206003193601126104f157600435600052600b602052602073ffffffffffffffffffffffffffffffffffffffff60406000205416604051908152f35b346104f15760206003193601126104f15761083061075a565b6001549073ffffffffffffffffffffffffffffffffffffffff80831633036104f1577fffffffffffffffffffffffff00000000000000000000000000000000000000009116911617600155600080f35b346104f15760406003193601126104f15761089961075a565b602435600091818352600760205273ffffffffffffffffffffffffffffffffffffffff806040852054169081156109df57808316928284146109db576109586109ac926108f0876000526007602052604060002090565b5416331460ff610949336109248873ffffffffffffffffffffffffffffffffffffffff16600052600c602052604060002090565b9073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b541681156109d3575b50612ee3565b61096c85600052600b602052604060002090565b9073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258480a480f35b905038610952565b8580fd5b8480fd5b346104f15760206003193601126104f15760043560005260166020526020604060002054604051908152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90633b9aca00811015610a55576003020190600090565b610a0f565b346104f15760406003193601126104f1576024356004356000526011602052604060002090633b9aca008110156104f157610a9491610a3e565b508054600182015460029092015460408051600f84810b8252608094851d900b602082015290810193909352606083015290f35b346104f15760406003193601126104f157610756610ae461075a565b60243580600052600760205273ffffffffffffffffffffffffffffffffffffffff806040600020541690831691610bf9610b9f84841495600960205261092486610b63610b4a604060002073ffffffffffffffffffffffffffffffffffffffff90541690565b73ffffffffffffffffffffffffffffffffffffffff1690565b1496610bd1610b4a610bb7610ba6610b9f876109248d73ffffffffffffffffffffffffffffffffffffffff16600052600a602052604060002090565b5460ff1690565b98600052600b602052604060002090565b5473ffffffffffffffffffffffffffffffffffffffff1690565b149573ffffffffffffffffffffffffffffffffffffffff16600052600c602052604060002090565b928415610c44575b508315610c3c575b508215610c34575b508115610c2c575b5060405190151581529081906020820190565b905038610c19565b915038610c11565b925038610c09565b935038610c01565b346104f15760006003193601126104f1576020610c68426132ed565b604051908152f35b346104f15760406003193601126104f157600435600052601160205260206001610ca06024356040600020610a3e565b500154604051908152f35b346104f15760006003193601126104f15760206040517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a8668152f35b60031960609101126104f15773ffffffffffffffffffffffffffffffffffffffff9060043582811681036104f1579160243590811681036104f1579060443590565b346104f157610d41610d3936610ce6565b9033926137c6565b005b346104f15760006003193601126104f1576020604051438152f35b346104f1576020806003193601126104f15760043590600654600160ff8216036104f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655610dbc610db783336138fc565b612eea565b610dd0826000526016602052604060002090565b541580159061103f575b61101557610dfa610df5836000526012602052604060002090565b612f20565b818101514210610feb57610e18610e128251600f0b90565b600f0b90565b92610e2161061d565b93610e496000958681528686820152610e44846000526012602052604060002090565b612f41565b610e7960155493610e62610e5d8487612fc1565b601555565b610e6a61061d565b90878252878783015284613b35565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815233600482015260248101829052918483604481897f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1948515610fe6577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c95610f7594610f2b928992610fb9575b5050612eea565b610f34816140d3565b6040805191825260208201839052429082015233907f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca9490606090a282612fc1565b60408051928352602083019190915290a1610fb660017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b80f35b610fd89250803d10610fdf575b610fd081836105dc565b810190612fce565b3880610f24565b503d610fc6565b612fe3565b60046040517ff6fafba0000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3ca9d617000000000000000000000000000000000000000000000000000000008152fd5b50611057610b9f836000526017602052604060002090565b610dda565b346104f15760206003193601126104f15761107561075a565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104f157167fffffffffffffffffffffffff00000000000000000000000000000000000000006002541617600255600080f35b346104f15760406003193601126104f15773ffffffffffffffffffffffffffffffffffffffff6110f361075a565b16600052600e60205260406000206024356000526020526020604060002054604051908152f35b346104f15760006003193601126104f157602060405160128152f35b346104f15760406003193601126104f15761114f61075a565b6024359073ffffffffffffffffffffffffffffffffffffffff6111728383612d31565b91166000526019602052600161119d819260406000209063ffffffff16600052602052604060002090565b019160009183546000925b8184106111ba57604051858152602090f35b909192936111d8826111cc8789612fef565b90549060031b1c61424a565b81018091116111ec579383019291906111a8565b612cb8565b346104f1576111ff36610ce6565b60405191602083019383851067ffffffffffffffff8611176105bb57610d41946040526000845261344b565b346104f15760406003193601126104f157602061125261124961075a565b602435906138fc565b6040519015158152f35b346104f15760206003193601126104f15760043560005260106020526020611291604060002054601183526040600020610a3e565b505460801d60405190600f0b8152f35b346104f15760006003193601126104f157602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b346104f15760206003193601126104f1576112ee61075a565b73ffffffffffffffffffffffffffffffffffffffff90816001541633036104f157167fffffffffffffffffffffffff00000000000000000000000000000000000000006000541617600055600080f35b6040519061134b8261059f565b600582527f312e302e300000000000000000000000000000000000000000000000000000006020830152565b346104f15760006003193601126104f15761075661074261133e565b346104f15760006003193601126104f157602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b346104f15760406003193601126104f15760043567ffffffffffffffff8082116104f157366023830112156104f15781600401359081116105bb578060051b916020926040519261141b60208301856105dc565b835260246020840191830101913683116104f157602401905b82821061144757610d4160243585613100565b81358152908401908401611434565b346104f15760206003193601126104f157602061147961147461075a565b61327d565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346104f15760206003193601126104f1576114b061075a565b73ffffffffffffffffffffffffffffffffffffffff8116156114d7575b610d419033614d6e565b50336114cd565b346104f15760006003193601126104f15760206040516104008152f35b346104f15760206003193601126104f1576004356000526007602052602073ffffffffffffffffffffffffffffffffffffffff60406000205416604051908152f35b346104f15760406003193601126104f157600654600160ff8216036104f15760209060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0080921617600655600161159a3360243560043561513e565b916006541617600655604051908152f35b346104f15760206003193601126104f157600435600052600d6020526020604060002054604051908152f35b346104f15760206003193601126104f15773ffffffffffffffffffffffffffffffffffffffff61160561075a565b16600052601a602052602063ffffffff60406000205416604051908152f35b346104f15760206003193601126104f15773ffffffffffffffffffffffffffffffffffffffff61165261075a565b1660005260086020526020604060002054604051908152f35b346104f15760206003193601126104f1576020610c686004356132ed565b346104f15760206003193601126104f15760043560005260146020526020604060002054600f0b604051908152f35b346104f15760206003193601126104f15773ffffffffffffffffffffffffffffffffffffffff6116e661075a565b16600052601b6020526020604060002054604051908152f35b346104f15760006003193601126104f157602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346104f15760406003193601126104f1576020610c6860243560043561541c565b346104f15760206003193601126104f1576004356000526017602052602060ff604060002054166040519015158152f35b346104f15760006003193601126104f1576020601354604051908152f35b346104f15760006003193601126104f1576107566040516117c38161059f565b600581527f766555534400000000000000000000000000000000000000000000000000000060208201526040519182916020835260208301906106cf565b346104f15760206003193601126104f1576107566118a960043561182743821115612eea565b6118a360135461183781846155f0565b9261185461184f856000526003602052604060002090565b6132b6565b936000928110156119035761184f61186e61187e92613007565b6000526003602052604060002090565b606085019081516060820193845182036118b9575b50505050505b6040830151613036565b9061530c565b6040519081529081906020820190565b6118f99550916118e360406118d56118e9946118f39796612fc1565b92015160408a015190612fc1565b906130b4565b9251905190612fc1565b906130c7565b3880808080611893565b50606084019081519043820361191c575b505050611899565b61194793506119316118f39261193f92612fc1565b6118e3604088015142612fc1565b915143612fc1565b388080611914565b346104f15760206003193601126104f15760043560009073ffffffffffffffffffffffffffffffffffffffff82541633036119cb5780825260166020526040822054907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116111ec5782526016602052604082205580f35b5080fd5b346104f15760206003193601126104f1576020610c686119ed61075a565b61330e565b346104f15760406003193601126104f157600435602435600654600160ff8216036104f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655611a4c610db783336138fc565b816000526012602052611a626040600020612f20565b90611a6e811515612eea565b8151600f0b15611abd576020820151421015610feb57611a8d9261458a565b610d4160017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006006541617600655565b60046040517fba112c93000000000000000000000000000000000000000000000000000000008152fd5b801515036104f157565b346104f15760406003193601126104f157611b0a61075a565b602435611b1681611ae7565b611ba481611b7473ffffffffffffffffffffffffffffffffffffffff851694611b4133871415612eea565b33600052600c60205260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b9060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b60405190151581527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b346104f15760406003193601126104f157600435600654600160ff8216036104f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217600655611c2b610db782336138fc565b611c42610df5826000526012602052604060002090565b611c62611c5d611c5460243542613036565b62093a80900490565b613084565b602082014281511115610feb578251600f0b15611abd5751811115611cc357611c8a42613015565b8111611c9957611a8d92614798565b60046040517fd6946a43000000000000000000000000000000000000000000000000000000008152fd5b60046040517fd5bb0fd9000000000000000000000000000000000000000000000000000000008152fd5b346104f15760406003193601126104f157602060ff611d5d611d0d61075a565b73ffffffffffffffffffffffffffffffffffffffff611d2a61077d565b9116600052600a845260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b54166040519015158152f35b346104f15760206003193601126104f157600435600052601260205260408060002060018154600f0b91015482519182526020820152f35b346104f15760806003193601126104f157611dba61075a565b611dc261077d565b6064359167ffffffffffffffff83116104f157366023840112156104f157826004013591611def83610639565b92611dfd60405194856105dc565b80845236602482870101116104f1576020816000926024610d41980183880137850101526044359161344b565b346104f15760206003193601126104f157600073ffffffffffffffffffffffffffffffffffffffff8154163303611e925760043581526017602052604081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905580f35b80fd5b346104f157600080600319360112611e925760405190611eb48261059f565b80825280602080930152806020604051611ecd8161059f565b8281520152611eda61398d565b50611ee361398d565b5060135491611ef061062c565b908282528260208301524260408301526060914360608201529184612119575b604083019182519285928442116120ed575b969390611f3662093a808395949504613084565b9787985b60ff8a10611f5a575b88610fb689611f558a61186e81601355565b613a57565b611f648991613026565b93888783964281116000146120ac5750610e1296611fc2611fbb611fd894611fb6611fae611fd196611fa8429e8f925b86019b611fa28d51600f0b90565b93612fc1565b906139b2565b9151600f0b90565b6139c9565b600f0b8d52565b8351600f0b613a10565b613a10565b600f0b8252565b81611fe7610e128b51600f0b90565b126120a4575b81611ffc610e128351600f0b90565b1261209c575b505082948383526120468289019761203f895161203961202b6120258a80612fc1565b8b6130b4565b670de0b6b3a7640000900490565b90613036565b8952613007565b9942850361206d5750505050505050610fb69293611f559143905290849338808080611f43565b6001919293949596975061208f89611f558d6000526003602052604060002090565b0198959291909493611f3a565b528738612002565b818952611fed565b9650611fd190611fc2611fbb611fd894611fb6611fae8c611fa8610e126120e76120e0846000526014602052604060002090565b54600f0b90565b98611f94565b9250612113612108612103606088015143612fc1565b61309c565b6118f3835142612fc1565b92611f22565b915061213261184f856000526003602052604060002090565b91611f10565b346104f15760c06003193601126104f15761215161075a565b602435906064359060443560ff831683036104f15773ffffffffffffffffffffffffffffffffffffffff9061219582841661218e33821415612ee3565b1515612ee3565b61219d610673565b948551602080970120956123306122136122fb6123076121bb61133e565b805190860120604080517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818901908152602081019e909e528d8201929092524660608e01523060808e01529b9093849060a0830190565b03936122457fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018352826105dc565b51902092898c516122b8816122ac8c8c8c8401968760609194939273ffffffffffffffffffffffffffffffffffffffff60808301967fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf845216602083015260408201520152565b038481018352826105dc565b5190208c51938491888301968790916042927f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201520190565b039081018352826105dc565b519020885190815260ff9097166020880152608435604088015260a43560608801526080870190565b866000978892838052039060015afa15610fe6578451928316156123ee576123788373ffffffffffffffffffffffffffffffffffffffff16600052601b602052604060002090565b9081549161238583613057565b9055036123c557421161239c5790610fb691614d6e565b600484517f0819bdcd000000000000000000000000000000000000000000000000000000008152fd5b600485517f756688fe000000000000000000000000000000000000000000000000000000008152fd5b600486517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b346104f15760206003193601126104f15760043573ffffffffffffffffffffffffffffffffffffffff612457610bb7836000526007602052604060002090565b161561254f576000612476610df5836000526012602052604060002090565b61249b610b4a610b4a60025473ffffffffffffffffffffffffffffffffffffffff1690565b6124a5428561424a565b6124b8610e1260208501519451600f0b90565b94612509604051968795869485947fdd9ec149000000000000000000000000000000000000000000000000000000008652600486019094939260609260808301968352602083015260408201520152565b03915afa8015610fe6576107569160009161252c575b5060405191829182610712565b61254991503d806000833e61254181836105dc565b810190613651565b3861251f565b60046040517f21f2b69c000000000000000000000000000000000000000000000000000000008152fd5b346104f15760406003193601126104f1576004356024356125a4826000526016602052604060002090565b5415801590612694575b61101557816125c282610d41941415612ee3565b6125d46125cf82336138fc565b612ee3565b6125e16125cf83336138fc565b6125f8610df5826000526012602052604060002090565b90612610610df5846000526012602052604060002090565b92612687612622610e128551600f0b90565b92612682602086015160208801518082101560001461268c5750955b61266861264961061d565b6000815260006020820152610e44856000526012602052604060002090565b61267061061d565b90600082526000602083015283613b35565b6140d3565b6148ad565b90509561263e565b506126ac610b9f836000526017602052604060002090565b6125ae565b346104f15760206003193601126104f157600435600052600360205260406000208054610756600260018401549301546040519383859460801d90600f0b85909493926060926080830196600f0b8352600f0b602083015260408201520152565b346104f15760406003193601126104f15761272b61075a565b60243561273781611ae7565b61279581611b7473ffffffffffffffffffffffffffffffffffffffff85169461276233871415612eea565b33600052600a60205260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b60405190151581527fccb38c6086037bf3d363115fc480b43f15aeb617ec6296b6a8ab6c70a1560adc60203392a3005b346104f15760606003193601126104f15760443573ffffffffffffffffffffffffffffffffffffffff811681036104f157600654600160ff8216036104f15760016128426107569360027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008095161760065560243560043561513e565b916006541617600655604051918291829190602083019252565b346104f15760406003193601126104f1576020610c6860243560043561424a565b346104f15760206003193601126104f15760043560005260106020526020604060002054604051908152f35b346104f15760006003193601126104f15760206040517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf8152f35b346104f15760206003193601126104f1576020610c686004356136b0565b346104f15760206003193601126104f1576004356000526009602052602073ffffffffffffffffffffffffffffffffffffffff60406000205416604051908152f35b346104f15760406003193601126104f157602060ff611d5d61296461075a565b73ffffffffffffffffffffffffffffffffffffffff61298161077d565b9116600052600c845260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b346104f15760406003193601126104f157600435602435600654600160ff8216036104f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00600291161760065581600052601260205260406000209060405191612a1e8361059f565b60018154600f0b9182855201549060208401918252612a3e831515612ee3565b15611abd5751421015610feb57611a8d92614a86565b346104f15760406003193601126104f157612a6d61075a565b602435600091818352600760205273ffffffffffffffffffffffffffffffffffffffff806040852054169081156109df57808316928284146109db57612ac4612ad8926108f0876000526007602052604060002090565b61096c856000526009602052604060002090565b7fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad8480a480f35b346104f15760406003193601126104f157612b1861075a565b6024359063ffffffff821682036104f15760209173ffffffffffffffffffffffffffffffffffffffff612b6792166000526019835260406000209063ffffffff16600052602052604060002090565b54604051908152f35b346104f15760206003193601126104f15760043560005260126020526020600160406000200154604051908152f35b346104f15760206003193601126104f15760043560009073ffffffffffffffffffffffffffffffffffffffff82541633036119cb578082526016602052604082205490600182018092116111ec5782526016602052604082205580f35b346104f15760006003193601126104f157602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba168152f35b346104f15760206003193601126104f157600073ffffffffffffffffffffffffffffffffffffffff8154163303611e9257600435815260176020526040812060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905580f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff63ffffffff809316019182116111ec57565b63ffffffff91821690821603919082116111ec57565b90612d69612d5f8373ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b5463ffffffff1690565b9163ffffffff80841615612ece5782612dc2612da58473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b612dae87612ce7565b63ffffffff16600052602052604060002090565b541115612ed75782612e06612df78473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b60008052602052604060002090565b5411612ece579190612e19600094612ce7565b83851684821611612e2b575050505090565b612e4d612e47612e3b8784612d1b565b60011c637fffffff1690565b82612d1b565b94612e9186612e7c8573ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b9063ffffffff16600052602052604060002090565b54848103612ea25750505050505090565b9380959692939410600014612ebc5750935b929190612e19565b949150612ec890612ce7565b90612eb4565b50505050600090565b50505061072390612ce7565b156104f157565b15612ef157565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b90604051612f2d8161059f565b6020600182948054600f0b84520154910152565b906020600191612f8d8151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b0151910155565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116111ec57565b919082039182116111ec57565b908160209103126104f1575161072381611ae7565b6040513d6000823e3d90fd5b8054821015610a555760005260206000200190600090565b90600182018092116111ec57565b906301dfe20082018092116111ec57565b9062093a8082018092116111ec57565b919082018092116111ec57565b8051821015610a555760209160051b010190565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146111ec5760010190565b9062093a80918281029281840414901517156111ec57565b90670de0b6b3a764000091808302928304036111ec57565b818102929181159184041417156111ec57565b81156130d1570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b613114826000526016602052604060002090565b5415801590613260575b6110155761312f6125cf83336138fc565b613146610bb7836000526007602052604060002090565b9061315e610df5846000526012602052604060002090565b602081015191613172610e128351600f0b90565b9061317e821515612ee3565b61318d610e5d83601554612fc1565b805195600093845b8881106132415750906126826131b09261266861264961061d565b42841115611cc3576131c142613015565b8411611c995760005b8681106131da5750505050505050565b60019061323b60056131f46131ef8254613057565b600555565b546131ff818a6142fd565b508761321e88613219613212878a613043565b518a6130b4565b6130c7565b613235610df5846000526012602052604060002090565b92614bfe565b016131ca565b946132596001916132528887613043565b5190613036565b9501613195565b50613278610b9f836000526017602052604060002090565b61311e565b73ffffffffffffffffffffffffffffffffffffffff80821660005260186020526040600020541680156000146132b1575090565b905090565b906040516132c3816105c0565b606060028294805480600f0b855260801d600f0b6020850152600181015460408501520154910152565b61072390601354600052600360205261330960406000206132b6565b61530c565b73ffffffffffffffffffffffffffffffffffffffff8116600052601a60205263ffffffff604060002054169081156133c35761337961336f60019273ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b612dae8394612ce7565b0160009181546000925b818410613391575050505090565b909192936133af6133a28684612fef565b9054429160031b1c61424a565b81018091116111ec57938301929190613383565b5050600090565b908160209103126104f15751610723816104c7565b9092610723949360809373ffffffffffffffffffffffffffffffffffffffff8092168452166020830152604082015281606082015201906106cf565b3d15613446573d9061342c82610639565b9161343a60405193846105dc565b82523d6000602084013e565b606090565b9291909161345b338385876137c6565b823b613468575b50505050565b6134be92602092600073ffffffffffffffffffffffffffffffffffffffff6040518097819682957f150b7a02000000000000000000000000000000000000000000000000000000009b8c855233600486016133df565b0393165af160009181613620575b5061356b576134d961341b565b80519081613566576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608490fd5b602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000160361359b5738808080613462565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4552433732313a2045524337323152656365697665722072656a65637465642060448201527f746f6b656e7300000000000000000000000000000000000000000000000000006064820152608490fd5b61364391925060203d60201161364a575b61363b81836105dc565b8101906133ca565b90386134cc565b503d613631565b6020818303126104f15780519067ffffffffffffffff82116104f1570181601f820112156104f157805161368481610639565b9261369260405194856105dc565b818452602082840101116104f15761072391602080850191016106ac565b80600052600d60205260406000205443146136d05761072390429061424a565b50600090565b6000818152600760205273ffffffffffffffffffffffffffffffffffffffff806040832054169081156137c257613717846000526007602052604060002090565b5416331460ff61374b336109248573ffffffffffffffffffffffffffffffffffffffff16600052600c602052604060002090565b541681156137ba575b50156119cb5761376e836000526009602052604060002090565b7fffffffffffffffffffffffff000000000000000000000000000000000000000081541690557fd2b1afce4dec934dea3cc952a83f031143c6b61041d6248bdf189f5c5efd07ad8280a4565b905038613754565b8280fd5b9092919260009284845260166020526040842054158015906138de575b61101557846137f1916138fc565b156137c2577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90848452600760205273ffffffffffffffffffffffffffffffffffffffff9061388d8661387e8460408920541696613853868216809914612eea565b828952600b6020526040892080548781166138b3575b50506138748361566e565b61147483826156c2565b6138878461327d565b90615a4a565b6138978682615ca9565b436138ac87600052600d602052604060002090565b55169280a4565b7fffffffffffffffffffffffff00000000000000000000000000000000000000001690553880613869565b5060ff6138f5866000526017602052604060002090565b54166137e3565b90600052600760205273ffffffffffffffffffffffffffffffffffffffff9060ff61397083604060002054169284811680851495600b602052604060002054161493600052600c60205260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b5416908215613985575b5081156132b1575090565b91503861397a565b6040519061399a826105c0565b60006060838281528260208201528260408201520152565b90600f0b90600f0b029081600f0b9182036111ec57565b90600f0b90600f0b03906f7fffffffffffffffffffffffffffffff82137fffffffffffffffffffffffffffffffff800000000000000000000000000000008312176111ec57565b90600f0b90600f0b01907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff8313176111ec57565b906060600291613aa38151600f0b85907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b60208101516fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000086549260801b169116178455604081015160018501550151910155565b9190613b065761062a91613a57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b613b3d61398d565b90613b4661398d565b926000806013549284151580613f95575b613b5f61062c565b94600086526020956000878201524260408201526060904360608201529082613f76575b60408201805191600091834211613f4f575b8a90613baa62093a8086989695949504613084565b966000975b60ff8910613e15575b5050509050613bec94508291508c8c87613bd4611f5596601355565b613d6c575b5050506000526003602052604060002090565b613bfb575b5050505050505050565b613c8597613c7f9685808094019542875111613d14575b5050500191825190428211613c92575b5050505050613c7a613c47613c41836000526010602052604060002090565b54613007565b9182613c5d826000526010602052604060002090565b554260408601524360608601526000526011602052604060002090565b610a3e565b90613af7565b3880808080808080613bf1565b5110613ca0575b8080613c22565b613cbd613ccf91613cb7613d0c95890151600f0b90565b906139c9565b91516000526014602052604060002090565b907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825416906fffffffffffffffffffffffffffffffff16179055565b388080613c99565b613d27613d2d92613d4e940151600f0b90565b90613a10565b83830151865114613d56575b613ccf86516000526014602052604060002090565b388281613c12565b613d6790613cb7858c0151600f0b90565b613d39565b613dbc613dc691613cb7611fae85613db5613dae613d9f613d938b613dcd9b0151600f0b90565b878c0151600f0b613cb7565b998d0199611fcc8b51600f0b90565b600f0b8952565b51600f0b90565b8651600f0b613a10565b600f0b8552565b6000613ddd610e128351600f0b90565b12613e0b575b506000613df4610e128551600f0b90565b12613e02575b888c8c613bd9565b60008352613dfa565b6000905238613de3565b613e1e90613026565b60009042811115613f1f5750611fd1613e6991611fc2613e62613e5842985b8d0196611fa8610e12613e518a51600f0b90565b928c612fc1565b8c51600f0b6139c9565b600f0b8b52565b6000613e79610e128951600f0b90565b12613f16575b6000613e8f610e128351600f0b90565b12613f0c575b5081938282528c613eb88289019761203f895161203961202b6120258a80612fc1565b98428503613edd5750505050505050613bec92611f5591439052903889818080613bb8565b60019192939495969750613eff89611f558c6000526003602052604060002090565b0197959092919493613baf565b6000905238613e95565b60008752613e7f565b93613e699150611fd190611fc2613e62613e58613f496120e08a6000526014602052604060002090565b93613e3d565b9150613f70613f65612103606087015143612fc1565b6118f3845142612fc1565b91613b95565b9050613f8f61184f836000526003602052604060002090565b90613b83565b6020820180514210806140bd575b614084575b8989602082019642885111908161406d575b5061401d575b5050613fda6120e082516000526014602052604060002090565b94519081613fea575b5050613b57565b51919350908103614000575082915b3880613fe3565b6120e0614017916000526014602052604060002090565b91613ff9565b61405161405f91613db5602061404761403a6140669751600f0b90565b6301dfe20090600f0b0590565b600f0b9201918252565b611fa8610e12428a51612fc1565b600f0b8a52565b8989613fc0565b905061407d610e128451600f0b90565b1338613fba565b6140b8613dae6140aa61409b61403a8751600f0b90565b600f0b60208c01908152613db5565b611fa8610e12428651612fc1565b613fa8565b50846140cd610e128551600f0b90565b13613fa3565b6140dd81336138fc565b15614220576000818152600760205273ffffffffffffffffffffffffffffffffffffffff806040832054169081156137c2573390614125856000526007602052604060002090565b54161460ff614158336109248573ffffffffffffffffffffffffffffffffffffffff16600052600c602052604060002090565b54168115614218575b50156119cb576141a661417e84600052600b602052604060002090565b7fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b8282827f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258280a46141d6836136d6565b6141e8836141e38361327d565b6158fc565b6141f283826156c2565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4565b905038614161565b60046040517fe433766c000000000000000000000000000000000000000000000000000000008152fd5b600081815260106020526040812054909181614267575050905090565b61428891613c7a614282926000526011602052604060002090565b506132b6565b6020810151916040820151938185820395128186128116918613901516176111ec576142d86142d16142c7610e129561072397600f0b90600f0b6139b2565b8451600f0b6139c9565b600f0b8352565b806142e48351600f0b90565b600f0b126142f5575b5051600f0b90565b8152386142ed565b73ffffffffffffffffffffffffffffffffffffffff908282821692614323841515612eea565b61432c8361327d565b90811615158080614568575b614373575b505061434891615ca9565b60007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4600190565b61437e575b8061433d565b90506143ad612d5f8273ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b63ffffffff8116156145335760016143f16143e88473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b612dae84612ce7565b015b60018061442c61440286615d7e565b612e7c8773ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b019180549061040061443d83613007565b11614509579088959493929160005b8281106144d257505050509161449c6144756144cc93614470876143489997615890565b6158e7565b9173ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b9063ffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000825416179055565b91614378565b8394959697506144fb6144f56144eb8385969795612fef565b90549060031b1c90565b87615890565b01908996959493929161444c565b60046040517f8c390496000000000000000000000000000000000000000000000000000000008152fd5b6001614562612df78473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b016143f3565b50821515614338565b6040519061457e8261059f565b60006020838281520152565b91906015549161459d610e5d8385613036565b6146006145a8614571565b916145b48151600f0b90565b926145cd6020830194855160208401528290600f0b9052565b6145e26142d186600f0b611fcc8551600f0b90565b6145fa82610e44896000526012602052604060002090565b86613b35565b81151580614790575b80614788575b6146a3575b5160408051948552602085018390526002908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361468a92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6249080608081015b0390a382613036565b604080519283526020830191909152819081015b0390a1565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390529390919060208560648160007f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1928315610fe65761475f61468a947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c97600091614769575b50612eea565b9192509350614614565b614782915060203d602011610fdf57610fd081836105dc565b38614759565b50600161460f565b506001614609565b601554906147a4614571565b93805190602081019182516020880152600f0b86528051600f0b907fffffffffffffffffffffffffffffffff8000000000000000000000000000000082126f7fffffffffffffffffffffffffffffff8313176111ec577f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9661469e9661484b938352806148a5575b5061484582610e44876000526012602052604060002090565b84613b35565b519060405190815260006020820152600360408201524260608201527fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62460803392a360408051828152602081019290925290918291820190565b84523861482c565b9291614924601554936148c3610e5d8587613036565b6148cb614571565b8151600f0b936148e96020840195865160208501528390600f0b9052565b6149056148fe87600f0b611fcc8651600f0b90565b600f0b8452565b80614a7e575b506145fa82610e44896000526012602052604060002090565b81151580614a76575b80614a6e575b6149a9575b5160408051948552602085018390526004908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361468a92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de624908060808101614681565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390529390919060208560648160007f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1928315610fe657614a6461468a947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c976000916147695750612eea565b9192509350614938565b506001614933565b50600061492d565b84523861490b565b919060155491614a99610e5d8385613036565b614aa46145a8614571565b81151580614bf6575b80614bee575b614b29575b5160408051948552602085018390526000908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361468a92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de624908060808101614681565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390529390919060208560648160007f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1928315610fe657614be461468a947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c976000916147695750612eea565b9192509350614ab8565b506001614ab3565b506001614aad565b9291614c14601554936148c3610e5d8587613036565b81151580614d66575b80614d5e575b614c99575b5160408051948552602085018390526005908501524260608501527f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c9361468a92919033907fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de624908060808101614681565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018390529390919060208560648160007f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1928315610fe657614d5461468a947f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c976000916147695750612eea565b9192509350614c28565b506000614c23565b506001614c1d565b9190614d798361327d565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260186020526040812080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168685161790559290848316928281168481847f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f8980a4848103614e0c575b505050505050509050565b614fe5575b505050614e23575b8080808080614e01565b614e50612d5f8373ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b9063ffffffff821615614fb0576001614e95614e8c8573ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b612dae85612ce7565b015b6001916001614ed2614ea887615d7e565b612e7c8873ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b0190614efe8873ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b5492805490610400614f108684613036565b116145095785835b838110614f9857505050505b828110614f495750505050614f4192935061447561449c916158e7565b803880614e19565b80614f92614f8b8693614f7c8c73ffffffffffffffffffffffffffffffffffffffff16600052600e602052604060002090565b90600052602052604060002090565b5484615890565b01614f24565b614fa86144f56144eb8386612fef565b018690614f18565b6001614fdf612df78573ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b01614e97565b615012612d5f8473ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b9163ffffffff83161561510857600161505761504e8673ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b612dae86612ce7565b01905b600192600161509561506b88615d7e565b612e7c8973ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b0191835493895b8581106150c257505050505050509061449c6144756150ba936158e7565b388080614e11565b806150d16144eb899385612fef565b84866150ea610bb7846000526007602052604060002090565b16036150f8575b500161509c565b6151029087615890565b386150f1565b6001615137612df78673ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b019061505a565b9042018042116111ec5762093a806151569104613084565b81156104f15742811115611cc35761516d42613015565b8111611c995761518c615181600554613057565b8094816005556142fd565b506151a4610df5846000526012602052604060002090565b906151f4601554926151b9610e5d8686613036565b6151c1614571565b8151600f0b936151df6020840195865160208501528390600f0b9052565b6149056148fe88600f0b611fcc8651600f0b90565b6040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018490529260208460648160007f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba73ffffffffffffffffffffffffffffffffffffffff165af1918215610fe6576152ac6152f8937f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c966000916147695750612eea565b516040517fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de62433918061468142878c84606091949392608082019582526020820152600160408201520152565b60408051928352602083019190915290a190565b9060408201615322611c5d825162093a80900490565b906000915b60ff8310615378575b5050505061535a61535a82600061534e610e126107239651600f0b90565b1261536f5751600f0b90565b6fffffffffffffffffffffffffffffffff1690565b60008152613db5565b61538190613026565b60009290848111156153fe575083925b60208601906153c96153c26153b86153aa8551600f0b90565b611fa8610e1289518b612fc1565b8951600f0b6139c9565b600f0b8852565b8585146153f757816153e66153ed92611fcc6001969551600f0b90565b600f0b9052565b8383520191615327565b5050615330565b92506154176120e0846000526014602052604060002090565b615391565b61542843831115612eea565b60009061543f816000526010602052604060002090565b546000905b60808210615580575b5050611fd161550d6154776142826155179695613c7a613db5966000526011602052604060002090565b936013549061548682826155f0565b61549d61184f826000526003602052604060002090565b9281101561555c5761184f61186e6154b492613007565b906154db60406154cd6060850151606087015190612fc1565b930151604085015190612fc1565b905b60408401519383615538575b50505050611fa8610e126155016020880151600f0b90565b92604088015190612fc1565b8351600f0b6139c9565b6000600f82900b126136d0576fffffffffffffffffffffffffffffffff1690565b612039926118e3615553969593606061321994015190612fc1565b388080806154e9565b5061556b606083015143612fc1565b9061557a604084015142612fc1565b906154dd565b9092818110156155ea576155a561559f61559a8484613036565b613007565b60011c90565b908560026155c184613c7a886000526011602052604060002090565b500154116155d65750600190935b0190615444565b9391506155e4600191612f94565b916155cf565b9261544d565b60009182915b60808310615605575b50505090565b90919282811015615668578281018082116111ec5760018082018092116111ec571c9082600261563f846000526003602052604060002090565b0154116156545750600190935b0191906155f6565b939250615662600191612f94565b9261564c565b926155ff565b60005260096020526040600020805473ffffffffffffffffffffffffffffffffffffffff811661569c575050565b7fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6000828152600760205273ffffffffffffffffffffffffffffffffffffffff6156f681604084205416918416809214612eea565b838252600760205261572d604083207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b8152600860205260408120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81019081116111ec57838261577c6157ee96600052600f602052604060002090565b548381036157fc57506157b66157c793614f7c8773ffffffffffffffffffffffffffffffffffffffff16600052600e602052604060002090565b55600052600f602052604060002090565b5573ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b6157f88154612f94565b9055565b836157b6916158656158356157c797614f7c8b73ffffffffffffffffffffffffffffffffffffffff16600052600e602052604060002090565b54806157b684614f7c8d73ffffffffffffffffffffffffffffffffffffffff16600052600e602052604060002090565b55614f7c8773ffffffffffffffffffffffffffffffffffffffff16600052600e602052604060002090565b8054680100000000000000008110156105bb576158b291600182018155612fef565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff829392549160031b92831b921b1916179055565b90600163ffffffff809316019182116111ec57565b90919073ffffffffffffffffffffffffffffffffffffffff811615158080615a41575b61592a575b50509050565b615935575b80615924565b615962612d5f8273ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b63ffffffff811615615a0b57600161599d6143e88473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b01935b60019060016159b161440286615d7e565b0186549160005b8381106159dd5750505050506159d692935061447561449c916158e7565b803861592f565b806159ec6144eb87938c612fef565b8381036159fb575b50016159b8565b615a059085615890565b386159f4565b6001615a3a612df78473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b01936159a0565b5083151561591f565b91909173ffffffffffffffffffffffffffffffffffffffff908184169181168281141580615ca0575b615a7f575b5050505050565b615b99575b50615a92575b808080615a78565b615abf612d5f8373ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b9063ffffffff821615615b64576001615afb614e8c8573ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b015b600180615b0c614ea887615d7e565b0191805490610400615b1d83613007565b116145095760005b828110615b4c5750505050615b45939261447061449c9361447593615890565b3880615a8a565b80615b5e6144f56144eb879486612fef565b01615b25565b6001615b93612df78573ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b01615afd565b615bc6612d5f8273ffffffffffffffffffffffffffffffffffffffff16600052601a602052604060002090565b63ffffffff811615615c6b576001615c016143e88473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b015b6001906001615c1461440286615d7e565b0181549160005b838110615c3d5750505050509061449c614475615c37936158e7565b38615a84565b80615c4c6144eb879385612fef565b8a8103615c5b575b5001615c1b565b615c659085615890565b38615c54565b6001615c9a612df78473ffffffffffffffffffffffffffffffffffffffff166000526019602052604060002090565b01615c03565b50831515615a73565b615d7491600081815260209060078252600f73ffffffffffffffffffffffffffffffffffffffff92604093615ce381868620541615612eea565b8584526007825284842080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff891617905586168352600881528383205494600e825284842086855282528085852055835252205573ffffffffffffffffffffffffffffffffffffffff166000526008602052604060002090565b6157f88154613007565b73ffffffffffffffffffffffffffffffffffffffff16600052601a60205263ffffffff6040600020541680151580615dbf575b156107235761072390612ce7565b5060196020526040600020615dd84291612dae84612ce7565b5414615db156fea164736f6c6343000818000a

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

0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba000000000000000000000000d03a0fd36f58edec885143a03fc546bc58d1794f

-----Decoded View---------------
Arg [0] : token_addr (address): 0x4D85bA8c3918359c78Ed09581E5bc7578ba932ba
Arg [1] : art_proxy (address): 0xd03a0fD36f58EDEc885143a03FC546BC58d1794F

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba
Arg [1] : 000000000000000000000000d03a0fd36f58edec885143a03fc546bc58d1794f


[ 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.