ETH Price: $3,110.59 (-5.52%)
Gas: 6 Gwei

Token

veAPHRA (veAPHRA)
 

Overview

Max Total Supply

0 veAPHRA

Holders

638

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Balance
0.000000000000000001 veAPHRA
0xE67E6c7c4a230818D6C4A3075894f12c8165B4BE
Loading...
Loading
Loading...
Loading
Loading...
Loading

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

Contract Source Code Verified (Exact Match)

Contract Name:
veAPHRA

Compiler Version
v0.8.11+commit.d7f03943

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 2 : Auth.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
    event OwnerUpdated(address indexed user, address indexed newOwner);

    event AuthorityUpdated(address indexed user, Authority indexed newAuthority);

    address public owner;

    Authority public authority;

    constructor(address _owner, Authority _authority) {
        owner = _owner;
        authority = _authority;

        emit OwnerUpdated(msg.sender, _owner);
        emit AuthorityUpdated(msg.sender, _authority);
    }

    modifier requiresAuth() {
        require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");

        _;
    }

    function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
        Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.

        // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
        // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
        return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
    }

    function setAuthority(Authority newAuthority) public virtual {
        // We check if the caller is the owner first because we want to ensure they can
        // always swap out the authority even if it's reverting or using up a lot of gas.
        require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));

        authority = newAuthority;

        emit AuthorityUpdated(msg.sender, newAuthority);
    }

    function setOwner(address newOwner) public virtual requiresAuth {
        owner = newOwner;

        emit OwnerUpdated(msg.sender, newOwner);
    }
}

/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
    function canCall(
        address user,
        address target,
        bytes4 functionSig
    ) external view returns (bool);
}

File 2 of 2 : veAPHRA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import {Auth, Authority} from "@rari-capital/solmate/src/auth/Auth.sol";

/**
@title Voting Escrow
@author Curve Finance
@license MIT
@notice Votes have a weight depending on time, so that users are
committed to the future of (whatever they are voting for)
@dev Vote weight decays linearly over time. Lock time cannot be
more than `MAXTIME` (4 years).

# Voting escrow to have time-weighted votes
# Votes have a weight depending on time, so that users are committed
# to the future of (whatever they are voting for).
# The weight in this implementation is linear, and lock cannot be more than maxtime:
# w ^
# 1 +        /
#   |      /
#   |    /
#   |  /
#   |/
# 0 +--------+------> time
#       maxtime (4 years?)
*/

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

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

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

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

        bytes memory table = TABLE;

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

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

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

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

                mstore(resultPtr, out)

                resultPtr := add(resultPtr, 4)
            }

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

            mstore(result, encodedLen)
        }

        return string(result);
    }
}

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

/**
* @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, uint indexed tokenId);

    /**
    * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
    */
    event Approval(address indexed owner, address indexed approved, uint 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 (uint balance);

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

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

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

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

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

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

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

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

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

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

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

/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
    /**
    * @dev Moves `amount` tokens from the caller's account to `recipient`.
    *
    * Returns a boolean value indicating whether the operation succeeded.
    *
    * Emits a {Transfer} event.
    */
    function transfer(address recipient, uint amount) external returns (bool);

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

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

    struct LockedBalance {
        int128 amount;
        uint end;
    }

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

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

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

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

    mapping(uint => uint) public ownership_change;

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

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

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

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

    string public badgeDescription;
    /// @dev Current count of token
    uint internal tokenId;

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

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

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

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

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

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

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

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

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

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

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

    /// @notice Contract constructor
    /// @param TOKEN_ADDR_ `ERC20APHRA` token address
    /// @param GOVERNANCE_ `GOVERNANCE`  address
    /// @param AUTHORITY_ `Authority`  address
    constructor(
        address TOKEN_ADDR_,
        address GOVERNANCE_,
        address AUTHORITY_
    ) Auth(GOVERNANCE_, Authority(AUTHORITY_)) {
        token = TOKEN_ADDR_;
        voter = msg.sender;
        point_history[0].blk = block.number;
        point_history[0].ts = block.timestamp;
        _unlocked = false;
        supportedInterfaces[ERC165_INTERFACE_ID] = true;
        supportedInterfaces[ERC721_INTERFACE_ID] = true;
        supportedInterfaces[ERC721_METADATA_INTERFACE_ID] = true;
        badgeDescription = string("APHRA Badges, can be used to boost gauge yields, vote on new token emissions, receive protocol bribes and participate in governance");
        // mint-ish
        emit Transfer(address(0), address(this), tokenId);
        // burn-ish
        emit Transfer(address(this), address(0), tokenId);
    }

    function isUnlocked() public view returns (bool) {
        return _unlocked;
    }

    function setBadgeDescription(string memory _newDescription) requiresAuth external {
        badgeDescription = _newDescription;
    }

    //todo setup so that this is hard coded to be veGovernor
    function unlock() public requiresAuth {
        require(_unlocked == false, "unlock already happened");
        _unlocked = true;
    }

    modifier unlocked() {
        require(_unlocked, "contract must be unlocked");
        _;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /// @dev Transfers the ownership of an NFT from one address to another address.
    ///      Throws unless `msg.sender` is the current owner, an authorized operator, or the
    ///      approved address for this NFT.
    ///      Throws if `_from` is not the current owner.
    ///      Throws if `_to` is the zero address.
    ///      Throws if `_tokenId` is not a valid NFT.
    ///      If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if
    ///      the return value is not `bytes4(keccak256("onERC721Received(address,address,uint,bytes)"))`.
    /// @param _from The current owner of the NFT.
    /// @param _to The new owner.
    /// @param _tokenId The NFT to transfer.
    /// @param _data Additional data with no specified format, sent in call to `_to`.
    function safeTransferFrom(
        address _from,
        address _to,
        uint _tokenId,
        bytes memory _data
    ) unlocked 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) {} catch (
                bytes memory reason
            ) {
                if (reason.length == 0) {
                    revert('ERC721: transfer to non ERC721Receiver implementer');
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }

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

    /// @dev Set or reaffirm the approved address for an NFT. The zero address indicates there is no approved address.
    ///      Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.
    ///      Throws if `_tokenId` is not a valid NFT. (NOTE: This is not written the EIP)
    ///      Throws if `_approved` is the current owner. (NOTE: This is not written the EIP)
    /// @param _approved Address to be approved for the given NFT ID.
    /// @param _tokenId ID of the token to be approved.
    function approve(address _approved, uint _tokenId) unlocked 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) unlocked external {
        // Throws if `_operator` is the `msg.sender`
        assert(_operator != msg.sender);
        ownerToOperators[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
    }

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

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

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

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

        Point memory last_point = Point({bias : 0, slope : 0, ts : block.timestamp, blk : block.number});
        if (_epoch > 0) {
            last_point = point_history[_epoch];
        }
        uint last_checkpoint = last_point.ts;
        // initial_last_point is used for extrapolation to calculate block number
        // (approximately, for *At methods) and save them
        // as we cannot figure that out exactly from inside the contract
        Point memory initial_last_point = last_point;
        uint block_slope = 0;
        // dblock/dt
        if (block.timestamp > last_point.ts) {
            block_slope = (MULTIPLIER * (block.number - last_point.blk)) / (block.timestamp - last_point.ts);
        }
        // If last point is already recorded in this block, slope=0
        // But that's ok b/c we know the block in such case

        // Go over weeks to fill history and calculate what the current point is
        {
            uint t_i = (last_checkpoint / WEEK) * WEEK;
            for (uint i = 0; i < 255; ++i) {
                // Hopefully it won't happen that this won't get used in 5 years!
                // If it does, users will be able to withdraw but vote weight will be broken
                t_i += WEEK;
                int128 d_slope = 0;
                if (t_i > block.timestamp) {
                    t_i = block.timestamp;
                } else {
                    d_slope = slope_changes[t_i];
                }
                last_point.bias -= last_point.slope * int128(int256(t_i - last_checkpoint));
                last_point.slope += d_slope;
                if (last_point.bias < 0) {
                    // This can happen
                    last_point.bias = 0;
                }
                if (last_point.slope < 0) {
                    // This cannot happen - just in case
                    last_point.slope = 0;
                }
                last_checkpoint = t_i;
                last_point.ts = t_i;
                last_point.blk = initial_last_point.blk + (block_slope * (t_i - initial_last_point.ts)) / MULTIPLIER;
                _epoch += 1;
                if (t_i == block.timestamp) {
                    last_point.blk = block.number;
                    break;
                } else {
                    point_history[_epoch] = last_point;
                }
            }
        }

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

        if (_tokenId != 0) {
            // If last point was in this block, the slope change has been applied already
            // But in such case we have 0 slope(s)
            last_point.slope += (u_new.slope - u_old.slope);
            last_point.bias += (u_new.bias - u_old.bias);
            if (last_point.slope < 0) {
                last_point.slope = 0;
            }
            if (last_point.bias < 0) {
                last_point.bias = 0;
            }
        }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /// @notice Deposit `_value` tokens for `_tokenId` and add to the lock
    /// @dev Anyone (even a smart contract) can deposit for someone else, but
    ///      cannot extend their locktime and deposit for a brand new user
    /// @param _tokenId lock NFT
    /// @param _value Amount to add to user's lock
    function deposit_for(uint _tokenId, uint _value) external nonreentrant {
        LockedBalance memory _locked = locked[_tokenId];

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

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

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

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

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

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

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

    /// @notice Deposit `_value` additional tokens for `_tokenId` without modifying the unlock time
    /// @param _value Amount of tokens to deposit and add to the lock
    function increase_amount(uint _tokenId, uint _value) external nonreentrant {
        assert(_isApprovedOrOwner(msg.sender, _tokenId));

        LockedBalance memory _locked = locked[_tokenId];

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

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

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

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

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

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

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

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

        locked[_tokenId] = LockedBalance(0, 0);
        uint supply_before = supply;
        supply = supply_before - value;

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

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

        // Burn the NFT
        _burn(_tokenId);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        Point memory point = point_history[target_epoch];
        uint dt = 0;
        if (target_epoch < _epoch) {
            Point memory point_next = point_history[target_epoch + 1];
            if (point.blk != point_next.blk) {
                dt = ((_block - point.blk) * (point_next.ts - point.ts)) / (point_next.blk - point.blk);
            }
        } else {
            if (point.blk != block.number) {
                dt = ((_block - point.blk) * (block.timestamp - point.ts)) / (block.number - point.blk);
            }
        }
        // Now dt contains info on how far are we beyond point
        return _supply_at(point, point.ts + dt);
    }

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

        string memory json = Base64.encode(bytes(string(abi.encodePacked('{"name": "Badge #', toString(_tokenId), '", "description": "', description, '", "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}'))));
        output = string(abi.encodePacked('data:application/json;base64,', json));
    }

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

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

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

        address owner = ownerOf(_tokenId);

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

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"TOKEN_ADDR_","type":"address"},{"internalType":"address","name":"GOVERNANCE_","type":"address"},{"internalType":"address","name":"AUTHORITY_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","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 veAPHRA.DepositType","name":"deposit_type","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"prevSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"supply","type":"uint256"}],"name":"Supply","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"abstain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"attach","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"attachments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"badgeDescription","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"balanceOfAtNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"balanceOfNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_t","type":"uint256"}],"name":"balanceOfNFTAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"block_number","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"create_lock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"create_lock_for","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"deposit_for","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"detach","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"get_last_user_slope","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increase_amount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_lock_duration","type":"uint256"}],"name":"increase_unlock_time","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isApprovedOrOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isUnlocked","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":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"contract Authority","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_newDescription","type":"string"}],"name":"setBadgeDescription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_voter","type":"address"}],"name":"setVoter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slope_changes","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_tokenIndex","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"t","type":"uint256"}],"name":"totalSupplyAtT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unlock","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"}]

60a06040526016805461ff0019166101001790553480156200002057600080fd5b50604051620047c9380380620047c9833981016040819052620000439162000315565b600080546001600160a01b03199081166001600160a01b0385811691821784556001805490931690851617909155604051849284929133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7691a36040516001600160a01b0382169033907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350506001600160a01b038316608052600c80546001600160a01b03191633179055437f54cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4fa55427f54cdd369e4e8a8515e52ca72ec816c2101831ad1f18bf44102ed171459c9b4f9556016805460ff19908116909155601560209081527f6d41d7311dcf06e9efee8fb0d881f048bb31170f670666b54beaa0a5d5c3d3e18054831660019081179091557f7c5380ac03aab70890fa83320849084ec294a4b2992171b171b58ecee9fd28868054841682179055635b5e139f60e01b6000527f57deef6d7eb0781ed7c6a908946a1ec625e2f9c98c7ba2b9a5beae2409ad58378054909316179091556040805160c0810190915260838082529091620047269083013980516200020a91600d9160209091019062000252565b50600e546040513090600090600080516020620047a9833981519152908290a4600e546040516000903090600080516020620047a9833981519152908390a45050506200039c565b82805462000260906200035f565b90600052602060002090601f016020900481019282620002845760008555620002cf565b82601f106200029f57805160ff1916838001178555620002cf565b82800160010185558215620002cf579182015b82811115620002cf578251825591602001919060010190620002b2565b50620002dd929150620002e1565b5090565b5b80821115620002dd5760008155600101620002e2565b80516001600160a01b03811681146200031057600080fd5b919050565b6000806000606084860312156200032b57600080fd5b6200033684620002f8565b92506200034660208501620002f8565b91506200035660408501620002f8565b90509250925092565b600181811c908216806200037457607f821691505b602082108114156200039657634e487b7160e01b600052602260045260246000fd5b50919050565b608051614360620003c66000396000818161098d01528181610dc30152612e4601526143606000f3fe608060405234801561001057600080fd5b50600436106103a45760003560e01c80638c2c9baf116101e9578063c2c4c5c11161010f578063e441135c116100ad578063f8a057631161007c578063f8a0576314610952578063fbd3a29d14610975578063fc0c546a14610988578063fd4a77f1146109af57600080fd5b8063e441135c146108d0578063e7e242d4146108f0578063e985e9c514610903578063ee99fe281461093f57600080fd5b8063d1c2babb116100e9578063d1c2babb14610859578063d1febfb91461086c578063d4e54c3b146108aa578063e0514aba146108bd57600080fd5b8063c2c4c5c11461082b578063c87b56dd14610833578063ccd1fe881461084657600080fd5b8063a183af5211610187578063b45a3c0e11610156578063b45a3c0e146107aa578063b88d4fde146107f2578063bf7e214f14610805578063c1f0fb9f1461081857600080fd5b8063a183af5214610769578063a22cb4651461077c578063a4d855df1461078f578063a69df4b5146107a257600080fd5b8063900cf0cf116101c3578063900cf0cf1461073a57806395d89b4114610402578063981b24d014610743578063986b7d8a1461075657600080fd5b80638c2c9baf146106f15780638da5cb5b146107045780638fbb38ff1461071757600080fd5b806342842e0e116102ce5780636352211e1161026c5780637116c60c1161023b5780637116c60c1461069d57806371197484146106b05780637a9e5e4b146106d35780638380edb7146106e657600080fd5b80636352211e1461062e57806365fc3873146106575780636f5488371461066a57806370a082311461068a57600080fd5b806346c96aac116102a857806346c96aac146105dc5780634bc2a657146105ef57806354fd4d50146106025780636347486a1461062657600080fd5b806342842e0e14610590578063430c2081146105a3578063461f711c146105b657600080fd5b806313af40351161034657806325a58b561161031557806325a58b56146105275780632e1a7d4d1461052d5780632f745c5914610540578063313ce5671461057657600080fd5b806313af4035146104e657806318160ddd146104f95780631c984bc31461050157806323b872dd1461051457600080fd5b8063081812fc11610382578063081812fc14610435578063095ea7b3146104765780630d6a20331461048b5780631376f3da146104ab57600080fd5b806301ffc9a7146103a9578063047fc9aa146103eb57806306fdde0314610402575b600080fd5b6103d66103b736600461371b565b6001600160e01b03191660009081526015602052604090205460ff1690565b60405190151581526020015b60405180910390f35b6103f460025481565b6040519081526020016103e2565b610428604051806040016040528060078152602001667665415048524160c81b81525081565b6040516103e29190613790565b61045e6104433660046137a3565b6000908152601060205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020016103e2565b6104896104843660046137d1565b6109c2565b005b6103f46104993660046137a3565b600a6020526000908152604090205481565b6104be6104b93660046137fd565b610ad6565b60408051600f95860b81529390940b60208401529282015260608101919091526080016103e2565b6104896104f436600461381f565b610b1d565b6103f4610b9a565b6103f461050f3660046137fd565b610baa565b61048961052236600461383c565b610bdd565b436103f4565b61048961053b3660046137a3565b610c10565b6103f461054e3660046137d1565b6001600160a01b03919091166000908152601260209081526040808320938352929052205490565b61057e601281565b60405160ff90911681526020016103e2565b61048961059e36600461383c565b610ee8565b6103d66105b13660046137d1565b610f25565b6105c96105c43660046137a3565b610f38565b604051600f9190910b81526020016103e2565b600c5461045e906001600160a01b031681565b6104896105fd36600461381f565b610f7b565b610428604051806040016040528060058152602001640312e302e360dc1b81525081565b610428610fb4565b61045e61063c3660046137a3565b6000908152600f60205260409020546001600160a01b031690565b6103f46106653660046137fd565b611042565b6103f46106783660046137a3565b60046020526000908152604090205481565b6103f461069836600461381f565b61108d565b6103f46106ab3660046137a3565b6110ab565b6105c96106be3660046137a3565b600960205260009081526040902054600f0b81565b6104896106e136600461381f565b61110b565b60165460ff166103d6565b6103f46106ff3660046137fd565b6111f5565b60005461045e906001600160a01b031681565b6103d66107253660046137a3565b600b6020526000908152604090205460ff1681565b6103f460055481565b6103f46107513660046137a3565b611201565b6104896107643660046137a3565b6113a3565b6104896107773660046137fd565b6113e7565b61048961078a36600461388b565b61150d565b61048961079d3660046137fd565b6115b4565b6104896117a2565b6107d86107b83660046137a3565b60036020526000908152604090208054600190910154600f9190910b9082565b60408051600f9390930b83526020830191909152016103e2565b610489610800366004613950565b611836565b60015461045e906001600160a01b031681565b6104896108263660046137a3565b611985565b6104896119b4565b6104286108413660046137a3565b6119f4565b6104896108543660046139d0565b611b2f565b6104896108673660046137fd565b611b78565b6104be61087a3660046137a3565b600660205260009081526040902080546001820154600290920154600f82810b93600160801b909304900b919084565b6103f46108b8366004613a19565b611d08565b6103f46108cb3660046137fd565b611d54565b6103f46108de3660046137a3565b60086020526000908152604090205481565b6103f46108fe3660046137a3565b611d60565b6103d6610911366004613a52565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205460ff1690565b61048961094d3660046137fd565b611d88565b6103f46109603660046137a3565b60009081526003602052604090206001015490565b6104896109833660046137a3565b611e71565b61045e7f000000000000000000000000000000000000000000000000000000000000000081565b6104896109bd3660046137a3565b611ea2565b60165460ff166109ed5760405162461bcd60e51b81526004016109e490613a80565b60405180910390fd5b6000818152600f60205260409020546001600160a01b031680610a0f57600080fd5b806001600160a01b0316836001600160a01b03161415610a2e57600080fd5b6000828152600f60209081526040808320546001600160a01b0385811685526014845282852033808752945291909320549216149060ff168180610a6f5750805b610a7857600080fd5b60008481526010602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918716917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050505050565b600760205281600052604060002081633b9aca008110610af557600080fd5b6003020180546001820154600290920154600f82810b9550600160801b90920490910b925084565b610b33336000356001600160e01b031916611ed4565b610b4f5760405162461bcd60e51b81526004016109e490613ab7565b600080546001600160a01b0319166001600160a01b0383169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b6000610ba5426110ab565b905090565b600082815260076020526040812082633b9aca008110610bcc57610bcc613add565b600302016001015490505b92915050565b60165460ff16610bff5760405162461bcd60e51b81526004016109e490613a80565b610c0b83838333611f7d565b505050565b60165460ff16610c325760405162461bcd60e51b81526004016109e490613a80565b601654610100900460ff16600114610c4957600080fd5b6016805461ff001916610200179055610c623382612043565b610c6e57610c6e613af3565b6000818152600a6020526040902054158015610c9957506000818152600b602052604090205460ff16155b610cb55760405162461bcd60e51b81526004016109e490613b09565b60008181526003602090815260409182902082518084019093528054600f0b835260010154908201819052421015610d285760405162461bcd60e51b8152602060048201526016602482015275546865206c6f636b206469646e27742065787069726560501b60448201526064016109e4565b8051604080518082018252600080825260208083018281528783526003909152929020905181546001600160801b0319166001600160801b039091161781559051600190910155600254600f9190910b90610d838282613b41565b6002556040805180820190915260008082526020820152610da790859085906120a9565b60405163a9059cbb60e01b8152336004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610e14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e389190613b58565b610e4457610e44613af3565b610e4d846126c8565b60408051858152602081018490524281830152905133917f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca94919081900360600190a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c81610ebb8482613b41565b6040805192835260208301919091520160405180910390a150506016805461ff0019166101001790555050565b60165460ff16610f0a5760405162461bcd60e51b81526004016109e490613a80565b610c0b83838360405180602001604052806000815250611836565b6000610f318383612043565b9392505050565b6000818152600860209081526040808320546007909252822081633b9aca008110610f6557610f65613add565b6003020154600160801b9004600f0b9392505050565b600c546001600160a01b03163314610f9257600080fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b600d8054610fc190613b75565b80601f0160208091040260200160405190810160405280929190818152602001828054610fed90613b75565b801561103a5780601f1061100f5761010080835404028352916020019161103a565b820191906000526020600020905b81548152906001019060200180831161101d57829003601f168201915b505050505081565b601654600090610100900460ff1660011461105c57600080fd5b6016805461ff001916610200179055611076838333612787565b90506016805461ff00191661010017905592915050565b6001600160a01b038116600090815260116020526040812054610bd7565b600554600081815260066020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015290919061110381856128ec565b949350505050565b6000546001600160a01b03163314806111a0575060015460405163b700961360e01b81526001600160a01b039091169063b70096139061115f90339030906001600160e01b03196000351690600401613bb0565b602060405180830381865afa15801561117c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111a09190613b58565b6111a957600080fd5b600180546001600160a01b0319166001600160a01b03831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350565b6000610f3183836129ee565b60004382111561121357611213613af3565b60055460006112228483612ccb565b600081815260066020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925083831015611331576000600681611285866001613bdd565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608083018290528501519192501461132b57826060015181606001516112f19190613b41565b836040015182604001516113059190613b41565b6060850151611314908a613b41565b61131e9190613bf5565b6113289190613c2a565b91505b50611380565b4382606001511461138057606082015161134b9043613b41565b604083015161135a9042613b41565b60608401516113699089613b41565b6113739190613bf5565b61137d9190613c2a565b90505b611399828284604001516113949190613bdd565b6128ec565b9695505050505050565b600c546001600160a01b031633146113ba57600080fd5b6000818152600a60205260409020546113d590600190613b41565b6000918252600a602052604090912055565b601654610100900460ff166001146113fe57600080fd5b6016805461ff0019166102001790556114173383612043565b61142357611423613af3565b60008281526003602090815260409182902082518084019093528054600f0b835260010154908201528115158061145d575060165460ff16155b61146957611469613af3565b60008160000151600f0b1380611482575060165460ff16155b6114c75760405162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b60448201526064016109e4565b428160200151116114ea5760405162461bcd60e51b81526004016109e490613c3e565b6114f983836000846002612d55565b50506016805461ff00191661010017905550565b60165460ff1661152f5760405162461bcd60e51b81526004016109e490613a80565b6001600160a01b03821633141561154857611548613af3565b3360008181526014602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b601654610100900460ff166001146115cb57600080fd5b6016805461ff0019166102001790556115e43383612043565b6115f0576115f0613af3565b600082815260036020908152604080832081518083019092528054600f0b825260010154918101919091529062093a808061162b8542613bdd565b6116359190613c2a565b61163f9190613bf5565b9050428260200151116116835760405162461bcd60e51b815260206004820152600c60248201526b131bd8dac8195e1c1a5c995960a21b60448201526064016109e4565b60008260000151600f0b136116ce5760405162461bcd60e51b8152602060048201526011602482015270139bdd1a1a5b99c81a5cc81b1bd8dad959607a1b60448201526064016109e4565b816020015181116117215760405162461bcd60e51b815260206004820152601f60248201527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e0060448201526064016109e4565b61172f6303c2670042613bdd565b81111561177e5760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652034207965617273206d6178000060448201526064016109e4565b61178d84600083856003612d55565b50506016805461ff0019166101001790555050565b6117b8336000356001600160e01b031916611ed4565b6117d45760405162461bcd60e51b81526004016109e490613ab7565b60165460ff16156118275760405162461bcd60e51b815260206004820152601760248201527f756e6c6f636b20616c72656164792068617070656e656400000000000000000060448201526064016109e4565b6016805460ff19166001179055565b60165460ff166118585760405162461bcd60e51b81526004016109e490613a80565b61186484848433611f7d565b823b1561197f57604051630a85bd0160e11b81526001600160a01b0384169063150b7a029061189d903390889087908790600401613c82565b6020604051808303816000875af19250505080156118d8575060408051601f3d908101601f191682019092526118d591810190613cb5565b60015b61197d573d808015611906576040519150601f19603f3d011682016040523d82523d6000602084013e61190b565b606091505b5080516119755760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b60648201526084016109e4565b805181602001fd5b505b50505050565b600c546001600160a01b0316331461199c57600080fd5b6000908152600b60205260409020805460ff19169055565b6119f2600060405180604001604052806000600f0b8152602001600081525060405180604001604052806000600f0b815260200160008152506120a9565b565b6000818152600f60205260409020546060906001600160a01b0316611a5b5760405162461bcd60e51b815260206004820152601b60248201527f517565727920666f72206e6f6e6578697374656e7420746f6b656e000000000060448201526064016109e4565b60008281526003602090815260409182902082518084019093528054600f0b83526001015490820152610f3183611a928142612f5a565b83602001518460000151600f0b600d8054611aac90613b75565b80601f0160208091040260200160405190810160405280929190818152602001828054611ad890613b75565b8015611b255780601f10611afa57610100808354040283529160200191611b25565b820191906000526020600020905b815481529060010190602001808311611b0857829003601f168201915b505050505061302c565b611b45336000356001600160e01b031916611ed4565b611b615760405162461bcd60e51b81526004016109e490613ab7565b8051611b7490600d906020840190613669565b5050565b60165460ff16611b9a5760405162461bcd60e51b81526004016109e490613a80565b6000828152600a6020526040902054158015611bc557506000828152600b602052604090205460ff16155b611be15760405162461bcd60e51b81526004016109e490613b09565b80821415611bee57600080fd5b611bf83383612043565b611c0157600080fd5b611c0b3382612043565b611c1457600080fd5b6000828152600360208181526040808420815180830183528154600f90810b825260019283015482860190815288885295855283872084518086019095528054820b855290920154938301849052805194519095929490910b921115611c7e578260200151611c84565b83602001515b604080518082018252600080825260208083018281528b835260038252848320935184546001600160801b0319166001600160801b0390911617845551600190930192909255825180840190935280835290820152909150611ce990879086906120a9565b611cf2866126c8565b611d00858383866004612d55565b505050505050565b601654600090610100900460ff16600114611d2257600080fd5b6016805461ff001916610200179055611d3c848484612787565b90506016805461ff0019166101001790559392505050565b6000610f318383612f5a565b600081815260046020526040812054431415611d7e57506000919050565b610bd78242612f5a565b601654610100900460ff16600114611d9f57600080fd5b6016805461ff00191661020017905560008281526003602090815260409182902082518084019093528054600f0b8352600101549082015281611de157600080fd5b60008160000151600f0b1380611dfa575060165460ff16155b611e3f5760405162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b60448201526064016109e4565b42816020015111611e625760405162461bcd60e51b81526004016109e490613c3e565b6114f983836000846000612d55565b600c546001600160a01b03163314611e8857600080fd5b6000818152600a60205260409020546113d5906001613bdd565b600c546001600160a01b03163314611eb957600080fd5b6000908152600b60205260409020805460ff19166001179055565b6001546000906001600160a01b03168015801590611f5e575060405163b700961360e01b81526001600160a01b0382169063b700961390611f1d90879030908890600401613bb0565b602060405180830381865afa158015611f3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f5e9190613b58565b8061110357506000546001600160a01b03858116911614949350505050565b6000828152600a6020526040902054158015611fa857506000828152600b602052604090205460ff16155b611fc45760405162461bcd60e51b81526004016109e490613b09565b611fce8183612043565b611fd757600080fd5b611fe18483613166565b611feb84836131cb565b611ff5838361324c565b6000828152600460205260408082204390555183916001600160a01b0380871692908816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a450505050565b6000818152600f602090815260408083205460108352818420546001600160a01b039182168086526014855283862088841680885295529285205492938085149392909116149060ff1682806120965750815b8061209e5750805b979650505050505050565b6040805160808101825260008082526020820181905291810182905260608101919091526040805160808101825260008082526020820181905291810182905260608101919091526005546000908190871561221557428760200151118015612119575060008760000151600f0b135b1561215e57865161212f906303c2670090613cd2565b600f0b602080870191909152870151612149904290613b41565b85602001516121589190613d10565b600f0b85525b428660200151118015612178575060008660000151600f0b135b156121bd57855161218e906303c2670090613cd2565b600f0b6020808601919091528601516121a8904290613b41565b84602001516121b79190613d10565b600f0b84525b602080880151600090815260098252604090205490870151600f9190910b935015612215578660200151866020015114156121fa57829150612215565b602080870151600090815260099091526040902054600f0b91505b60408051608081018252600080825260208201524291810191909152436060820152811561228a575060008181526006602090815260409182902082516080810184528154600f81810b8352600160801b909104900b9281019290925260018101549282019290925260029091015460608201525b6040810151816000428310156122d75760408401516122a99042613b41565b60608501516122b89043613b41565b6122ca90670de0b6b3a7640000613bf5565b6122d49190613c2a565b90505b600062093a806122e78186613c2a565b6122f19190613bf5565b905060005b60ff81101561246c5761230c62093a8083613bdd565b915060004283111561232057429250612334565b50600082815260096020526040902054600f0b5b61233e8684613b41565b876020015161234d9190613d10565b8751889061235c908390613da5565b600f0b905250602087018051829190612376908390613df5565b600f90810b90915288516000910b1215905061239157600087525b60008760200151600f0b12156123a957600060208801525b60408088018490528501519295508592670de0b6b3a7640000906123cd9085613b41565b6123d79086613bf5565b6123e19190613c2a565b85606001516123f09190613bdd565b6060880152612400600189613bdd565b975042831415612416575043606087015261246c565b6000888152600660209081526040918290208951918a01516001600160801b03908116600160801b02921691909117815590880151600182015560608801516002909101555061246581613e44565b90506122f6565b505060058590558b156124f7578860200151886020015161248d9190613da5565b8460200181815161249e9190613df5565b600f0b905250885188516124b29190613da5565b845185906124c1908390613df5565b600f90810b90915260208601516000910b121590506124e257600060208501525b60008460000151600f0b12156124f757600084525b6000858152600660209081526040918290208651918701516001600160801b03908116600160801b02921691909117815590850151600182015560608501516002909101558b156126ba57428b6020015111156125af57602089015161255d9088613df5565b96508a602001518a60200151141561258157602088015161257e9088613da5565b96505b60208b810151600090815260099091526040902080546001600160801b0319166001600160801b0389161790555b428a60200151111561260a578a602001518a60200151111561260a5760208801516125da9087613da5565b60208b810151600090815260099091526040902080546001600160801b0319166001600160801b03831617905595505b60008c815260086020526040812054612624906001613bdd565b905080600860008f815260200190815260200160002081905550428960400181815250504389606001818152505088600760008f815260200190815260200160002082633b9aca00811061267a5761267a613add565b825160208401516001600160801b03908116600160801b029116176003919091029190910190815560408201516001820155606090910151600290910155505b505050505050505050505050565b6126d23382612043565b61271e5760405162461bcd60e51b815260206004820181905260248201527f63616c6c6572206973206e6f74206f776e6572206e6f7220617070726f76656460448201526064016109e4565b6000818152600f60205260408120546001600160a01b03169061274190836109c2565b61274b33836131cb565b60405182906000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b60008062093a80806127998642613bdd565b6127a39190613c2a565b6127ad9190613bf5565b905060008511806127c1575060165460ff16155b6127ca57600080fd5b4281116128285760405162461bcd60e51b815260206004820152602660248201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060448201526566757475726560d01b60648201526084016109e4565b6128366303c2670042613bdd565b8111156128855760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652032207965617273206d6178000060448201526064016109e4565b600e6000815461289490613e44565b90915550600e546128a584826132e2565b5060008181526003602090815260409182902082518084019093528054600f0b8352600190810154918301919091526128e391839189918691612d55565b95945050505050565b600080839050600062093a808083604001516129089190613c2a565b6129129190613bf5565b905060005b60ff8110156129c65761292d62093a8083613bdd565b915060008583111561294157859250612955565b50600082815260096020526040902054600f0b5b60408401516129649084613b41565b84602001516129739190613d10565b84518590612982908390613da5565b600f0b9052508286141561299657506129c6565b80846020018181516129a89190613df5565b600f0b90525050604083018290526129bf81613e44565b9050612917565b5060008260000151600f0b12156129dc57600082525b50516001600160801b03169392505050565b600043821115612a0057612a00613af3565b600083815260086020526040812054815b6080811015612aa457818310612a2657612aa4565b60006002612a348486613bdd565b612a3f906001613bdd565b612a499190613c2a565b6000888152600760205260409020909150869082633b9aca008110612a7057612a70613add565b600302016002015411612a8557809350612a93565b612a90600182613b41565b92505b50612a9d81613e44565b9050612a11565b50600085815260076020526040812083633b9aca008110612ac757612ac7613add565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b602083015260018101549282019290925260029091015460608201526005549091506000612b1c8783612ccb565b600081815260066020908152604080832081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608201529192508084841015612bfb576000600681612b80876001613bdd565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b93810193909352600181015491830191909152600201546060808301829052860151919250612bdd9190613b41565b925083604001518160400151612bf39190613b41565b915050612c1f565b6060830151612c0a9043613b41565b9150826040015142612c1c9190613b41565b90505b60408301518215612c5c578284606001518c612c3b9190613b41565b612c459084613bf5565b612c4f9190613c2a565b612c599082613bdd565b90505b6040870151612c6b9082613b41565b8760200151612c7a9190613d10565b87518890612c89908390613da5565b600f90810b90915288516000910b129050612cb957505093516001600160801b03169650610bd795505050505050565b60009950505050505050505050610bd7565b60008082815b6080811015612d4b57818310612ce657612d4b565b60006002612cf48486613bdd565b612cff906001613bdd565b612d099190613c2a565b6000818152600660205260409020600201549091508710612d2c57809350612d3a565b612d37600182613b41565b92505b50612d4481613e44565b9050612cd1565b5090949350505050565b6002548290612d648682613bdd565b6002556040805180820190915260008082526020820152825160208085015190830152600f0b8152825187908490612d9d908390613df5565b600f0b9052508515612db157602083018690525b6000888152600360209081526040909120845181546001600160801b0319166001600160801b0390911617815590840151600190910155612df38882856120a9565b338715801590612e1557506004856004811115612e1257612e12613e5f565b14155b15612ebf576040516323b872dd60e01b81526001600160a01b038281166004830152306024830152604482018a90527f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303816000875af1158015612e8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eb39190613b58565b612ebf57612ebf613af3565b8360200151816001600160a01b03167fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6248b8b8942604051612f039493929190613e75565b60405180910390a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c83612f378a82613bdd565b6040805192835260208301919091520160405180910390a1505050505050505050565b60008281526008602052604081205480612f78576000915050610bd7565b600084815260076020526040812082633b9aca008110612f9a57612f9a613add565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b602083015260018101549282018390526002015460608201529150612fe69085613eb3565b8160200151612ff59190613d10565b81518290613004908390613da5565b600f90810b90915282516000910b1215905061301f57600081525b51600f0b9150610bd79050565b606060405180610120016040528060fd81526020016141ee60fd913990508061305487613345565b604051602001613065929190613ef2565b60405160208183030381529060405290508061308086613345565b604051602001613091929190613f6e565b6040516020818303038152906040529050806130ac85613345565b6040516020016130bd929190613fee565b6040516020818303038152906040529050806130d884613345565b6040516020016130e992919061406f565b6040516020818303038152906040529050600061313861310888613345565b8461311285613443565b604051602001613124939291906140ca565b604051602081830303815290604052613443565b90508060405160200161314b9190614194565b60405160208183030381529060405291505095945050505050565b6000818152600f60205260409020546001600160a01b0383811691161461318f5761318f613af3565b6000818152601060205260409020546001600160a01b031615611b7457600090815260106020526040902080546001600160a01b031916905550565b6000818152600f60205260409020546001600160a01b038381169116146131f4576131f4613af3565b6000818152600f6020526040902080546001600160a01b031916905561321a82826135a9565b6001600160a01b0382166000908152601160205260408120805460019290613243908490613b41565b90915550505050565b6000818152600f60205260409020546001600160a01b03161561327157613271613af3565b6000818152600f6020908152604080832080546001600160a01b0319166001600160a01b038716908117909155808452601180845282852080546012865284872081885286528487208890558787526013865293862093909355908452909152805460019290613243908490613bdd565b60006001600160a01b0383166132fa576132fa613af3565b613304838361324c565b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a450600192915050565b6060816133695750506040805180820190915260018152600360fc1b602082015290565b8160005b8115613393578061337d81613e44565b915061338c9050600a83613c2a565b915061336d565b60008167ffffffffffffffff8111156133ae576133ae6138c4565b6040519080825280601f01601f1916602001820160405280156133d8576020820181803683370190505b5090505b8415611103576133ed600183613b41565b91506133fa600a866141d9565b613405906030613bdd565b60f81b81838151811061341a5761341a613add565b60200101906001600160f81b031916908160001a90535061343c600a86613c2a565b94506133dc565b805160609080613463575050604080516020810190915260008152919050565b60006003613472836002613bdd565b61347c9190613c2a565b613487906004613bf5565b90506000613496826020613bdd565b67ffffffffffffffff8111156134ae576134ae6138c4565b6040519080825280601f01601f1916602001820160405280156134d8576020820181803683370190505b50905060006040518060600160405280604081526020016142eb604091399050600181016020830160005b86811015613564576003818a01810151603f601282901c8116860151600c83901c8216870151600684901c831688015192909316870151600891821b60ff94851601821b92841692909201901b91160160e01b835260049092019101613503565b50600386066001811461357e576002811461358f5761359b565b613d3d60f01b60011983015261359b565b603d60f81b6000198301525b505050918152949350505050565b6001600160a01b0382166000908152601160205260408120546135ce90600190613b41565b6000838152601360205260409020549091508082141561361e576001600160a01b03841660009081526012602090815260408083208584528252808320839055858352601390915281205561197f565b6001600160a01b039390931660009081526012602090815260408083209383529281528282208054868452848420819055835260139091528282209490945592839055908252812055565b82805461367590613b75565b90600052602060002090601f01602090048101928261369757600085556136dd565b82601f106136b057805160ff19168380011785556136dd565b828001600101855582156136dd579182015b828111156136dd5782518255916020019190600101906136c2565b506136e99291506136ed565b5090565b5b808211156136e957600081556001016136ee565b6001600160e01b03198116811461371857600080fd5b50565b60006020828403121561372d57600080fd5b8135610f3181613702565b60005b8381101561375357818101518382015260200161373b565b8381111561197f5750506000910152565b6000815180845261377c816020860160208601613738565b601f01601f19169290920160200192915050565b602081526000610f316020830184613764565b6000602082840312156137b557600080fd5b5035919050565b6001600160a01b038116811461371857600080fd5b600080604083850312156137e457600080fd5b82356137ef816137bc565b946020939093013593505050565b6000806040838503121561381057600080fd5b50508035926020909101359150565b60006020828403121561383157600080fd5b8135610f31816137bc565b60008060006060848603121561385157600080fd5b833561385c816137bc565b9250602084013561386c816137bc565b929592945050506040919091013590565b801515811461371857600080fd5b6000806040838503121561389e57600080fd5b82356138a9816137bc565b915060208301356138b98161387d565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff808411156138f5576138f56138c4565b604051601f8501601f19908116603f0116810190828211818310171561391d5761391d6138c4565b8160405280935085815286868601111561393657600080fd5b858560208301376000602087830101525050509392505050565b6000806000806080858703121561396657600080fd5b8435613971816137bc565b93506020850135613981816137bc565b925060408501359150606085013567ffffffffffffffff8111156139a457600080fd5b8501601f810187136139b557600080fd5b6139c4878235602084016138da565b91505092959194509250565b6000602082840312156139e257600080fd5b813567ffffffffffffffff8111156139f957600080fd5b8201601f81018413613a0a57600080fd5b611103848235602084016138da565b600080600060608486031215613a2e57600080fd5b83359250602084013591506040840135613a47816137bc565b809150509250925092565b60008060408385031215613a6557600080fd5b8235613a70816137bc565b915060208301356138b9816137bc565b60208082526019908201527f636f6e7472616374206d75737420626520756e6c6f636b656400000000000000604082015260600190565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b602080825260089082015267185d1d1858da195960c21b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082821015613b5357613b53613b2b565b500390565b600060208284031215613b6a57600080fd5b8151610f318161387d565b600181811c90821680613b8957607f821691505b60208210811415613baa57634e487b7160e01b600052602260045260246000fd5b50919050565b6001600160a01b0393841681529190921660208201526001600160e01b0319909116604082015260600190565b60008219821115613bf057613bf0613b2b565b500190565b6000816000190483118215151615613c0f57613c0f613b2b565b500290565b634e487b7160e01b600052601260045260246000fd5b600082613c3957613c39613c14565b500490565b60208082526024908201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686040820152636472617760e01b606082015260800190565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061139990830184613764565b600060208284031215613cc757600080fd5b8151610f3181613702565b600081600f0b83600f0b80613ce957613ce9613c14565b60016001607f1b0319821460001982141615613d0757613d07613b2b565b90059392505050565b600081600f0b83600f0b60016001607f1b03600082136000841383830485118282161615613d4057613d40613b2b565b60016001607f1b03196000851282811687830587121615613d6357613d63613b2b565b60008712925085820587128484161615613d7f57613d7f613b2b565b85850587128184161615613d9557613d95613b2b565b5050509290910295945050505050565b600081600f0b83600f0b600081128160016001607f1b031901831281151615613dd057613dd0613b2b565b8160016001607f1b03018313811615613deb57613deb613b2b565b5090039392505050565b600081600f0b83600f0b600082128260016001607f1b0303821381151615613e1f57613e1f613b2b565b8260016001607f1b0319038212811615613e3b57613e3b613b2b565b50019392505050565b6000600019821415613e5857613e58613b2b565b5060010190565b634e487b7160e01b600052602160045260246000fd5b848152602081018490526080810160058410613ea157634e487b7160e01b600052602160045260246000fd5b60408201939093526060015292915050565b60008083128015600160ff1b850184121615613ed157613ed1613b2b565b6001600160ff1b0384018313811615613eec57613eec613b2b565b50500390565b60008351613f04818460208801613738565b6503a37b5b2b7160d51b9083019081528351613f27816006840160208801613738565b7f3c2f746578743e3c7465787420783d2231302220793d2234302220636c61737360069290910191820152671e913130b9b2911f60c11b6026820152602e01949350505050565b60008351613f80818460208801613738565b6903130b630b731b2a7b3160b51b9083019081528351613fa781600a840160208801613738565b7f3c2f746578743e3c7465787420783d2231302220793d2236302220636c617373600a9290910191820152671e913130b9b2911f60c11b602a820152603201949350505050565b60008351614000818460208801613738565b6a03637b1b5b2b22fb2b732160ad1b908301908152835161402881600b840160208801613738565b7f3c2f746578743e3c7465787420783d2231302220793d2238302220636c617373600b9290910191820152671e913130b9b2911f60c11b602b820152603301949350505050565b60008351614081818460208801613738565b6503b30b63ab2960d51b90830190815283516140a4816006840160208801613738565b6c1e17ba32bc3a1f1e17b9bb339f60991b60069290910191820152601301949350505050565b707b226e616d65223a20224261646765202360781b815283516000906140f7816011850160208901613738565b72111610113232b9b1b934b83a34b7b7111d101160691b601191840191820152845161412a816024840160208901613738565b7f222c2022696d616765223a2022646174613a696d6167652f7376672b786d6c3b602492909101918201526618985cd94d8d0b60ca1b6044820152835161417881604b840160208801613738565b61227d60f01b604b9290910191820152604d0195945050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516141cc81601d850160208701613738565b91909101601d0192915050565b6000826141e8576141e8613c14565b50069056fe3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f73766722207072657365727665417370656374526174696f3d22784d696e594d696e206d656574222076696577426f783d223020302033353020333530223e3c7374796c653e2e62617365207b2066696c6c3a2077686974653b20666f6e742d66616d696c793a2073657269663b20666f6e742d73697a653a20313470783b207d3c2f7374796c653e3c726563742077696474683d223130302522206865696768743d2231303025222066696c6c3d22626c61636b22202f3e3c7465787420783d2231302220793d2232302220636c6173733d2262617365223e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa264697066735822122067c5a9ff4136c7ab55ac4f81e85ab66a538b3e1b8b701bb048164a97fc26971564736f6c634300080b00334150485241204261646765732c2063616e206265207573656420746f20626f6f7374206761756765207969656c64732c20766f7465206f6e206e657720746f6b656e20656d697373696f6e732c20726563656976652070726f746f636f6c2062726962657320616e6420706172746963697061746520696e20676f7665726e616e6365ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef000000000000000000000000fd7c8060e57c5c78a582fa0dcd3d549db0a780f60000000000000000000000004d03fb78bda67a04f1bd6fde5024759d8ce8d866000000000000000000000000cbb46b017e8d785c107e97c56135894b3ead599c

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103a45760003560e01c80638c2c9baf116101e9578063c2c4c5c11161010f578063e441135c116100ad578063f8a057631161007c578063f8a0576314610952578063fbd3a29d14610975578063fc0c546a14610988578063fd4a77f1146109af57600080fd5b8063e441135c146108d0578063e7e242d4146108f0578063e985e9c514610903578063ee99fe281461093f57600080fd5b8063d1c2babb116100e9578063d1c2babb14610859578063d1febfb91461086c578063d4e54c3b146108aa578063e0514aba146108bd57600080fd5b8063c2c4c5c11461082b578063c87b56dd14610833578063ccd1fe881461084657600080fd5b8063a183af5211610187578063b45a3c0e11610156578063b45a3c0e146107aa578063b88d4fde146107f2578063bf7e214f14610805578063c1f0fb9f1461081857600080fd5b8063a183af5214610769578063a22cb4651461077c578063a4d855df1461078f578063a69df4b5146107a257600080fd5b8063900cf0cf116101c3578063900cf0cf1461073a57806395d89b4114610402578063981b24d014610743578063986b7d8a1461075657600080fd5b80638c2c9baf146106f15780638da5cb5b146107045780638fbb38ff1461071757600080fd5b806342842e0e116102ce5780636352211e1161026c5780637116c60c1161023b5780637116c60c1461069d57806371197484146106b05780637a9e5e4b146106d35780638380edb7146106e657600080fd5b80636352211e1461062e57806365fc3873146106575780636f5488371461066a57806370a082311461068a57600080fd5b806346c96aac116102a857806346c96aac146105dc5780634bc2a657146105ef57806354fd4d50146106025780636347486a1461062657600080fd5b806342842e0e14610590578063430c2081146105a3578063461f711c146105b657600080fd5b806313af40351161034657806325a58b561161031557806325a58b56146105275780632e1a7d4d1461052d5780632f745c5914610540578063313ce5671461057657600080fd5b806313af4035146104e657806318160ddd146104f95780631c984bc31461050157806323b872dd1461051457600080fd5b8063081812fc11610382578063081812fc14610435578063095ea7b3146104765780630d6a20331461048b5780631376f3da146104ab57600080fd5b806301ffc9a7146103a9578063047fc9aa146103eb57806306fdde0314610402575b600080fd5b6103d66103b736600461371b565b6001600160e01b03191660009081526015602052604090205460ff1690565b60405190151581526020015b60405180910390f35b6103f460025481565b6040519081526020016103e2565b610428604051806040016040528060078152602001667665415048524160c81b81525081565b6040516103e29190613790565b61045e6104433660046137a3565b6000908152601060205260409020546001600160a01b031690565b6040516001600160a01b0390911681526020016103e2565b6104896104843660046137d1565b6109c2565b005b6103f46104993660046137a3565b600a6020526000908152604090205481565b6104be6104b93660046137fd565b610ad6565b60408051600f95860b81529390940b60208401529282015260608101919091526080016103e2565b6104896104f436600461381f565b610b1d565b6103f4610b9a565b6103f461050f3660046137fd565b610baa565b61048961052236600461383c565b610bdd565b436103f4565b61048961053b3660046137a3565b610c10565b6103f461054e3660046137d1565b6001600160a01b03919091166000908152601260209081526040808320938352929052205490565b61057e601281565b60405160ff90911681526020016103e2565b61048961059e36600461383c565b610ee8565b6103d66105b13660046137d1565b610f25565b6105c96105c43660046137a3565b610f38565b604051600f9190910b81526020016103e2565b600c5461045e906001600160a01b031681565b6104896105fd36600461381f565b610f7b565b610428604051806040016040528060058152602001640312e302e360dc1b81525081565b610428610fb4565b61045e61063c3660046137a3565b6000908152600f60205260409020546001600160a01b031690565b6103f46106653660046137fd565b611042565b6103f46106783660046137a3565b60046020526000908152604090205481565b6103f461069836600461381f565b61108d565b6103f46106ab3660046137a3565b6110ab565b6105c96106be3660046137a3565b600960205260009081526040902054600f0b81565b6104896106e136600461381f565b61110b565b60165460ff166103d6565b6103f46106ff3660046137fd565b6111f5565b60005461045e906001600160a01b031681565b6103d66107253660046137a3565b600b6020526000908152604090205460ff1681565b6103f460055481565b6103f46107513660046137a3565b611201565b6104896107643660046137a3565b6113a3565b6104896107773660046137fd565b6113e7565b61048961078a36600461388b565b61150d565b61048961079d3660046137fd565b6115b4565b6104896117a2565b6107d86107b83660046137a3565b60036020526000908152604090208054600190910154600f9190910b9082565b60408051600f9390930b83526020830191909152016103e2565b610489610800366004613950565b611836565b60015461045e906001600160a01b031681565b6104896108263660046137a3565b611985565b6104896119b4565b6104286108413660046137a3565b6119f4565b6104896108543660046139d0565b611b2f565b6104896108673660046137fd565b611b78565b6104be61087a3660046137a3565b600660205260009081526040902080546001820154600290920154600f82810b93600160801b909304900b919084565b6103f46108b8366004613a19565b611d08565b6103f46108cb3660046137fd565b611d54565b6103f46108de3660046137a3565b60086020526000908152604090205481565b6103f46108fe3660046137a3565b611d60565b6103d6610911366004613a52565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205460ff1690565b61048961094d3660046137fd565b611d88565b6103f46109603660046137a3565b60009081526003602052604090206001015490565b6104896109833660046137a3565b611e71565b61045e7f000000000000000000000000fd7c8060e57c5c78a582fa0dcd3d549db0a780f681565b6104896109bd3660046137a3565b611ea2565b60165460ff166109ed5760405162461bcd60e51b81526004016109e490613a80565b60405180910390fd5b6000818152600f60205260409020546001600160a01b031680610a0f57600080fd5b806001600160a01b0316836001600160a01b03161415610a2e57600080fd5b6000828152600f60209081526040808320546001600160a01b0385811685526014845282852033808752945291909320549216149060ff168180610a6f5750805b610a7857600080fd5b60008481526010602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918716917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a45050505050565b600760205281600052604060002081633b9aca008110610af557600080fd5b6003020180546001820154600290920154600f82810b9550600160801b90920490910b925084565b610b33336000356001600160e01b031916611ed4565b610b4f5760405162461bcd60e51b81526004016109e490613ab7565b600080546001600160a01b0319166001600160a01b0383169081178255604051909133917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a350565b6000610ba5426110ab565b905090565b600082815260076020526040812082633b9aca008110610bcc57610bcc613add565b600302016001015490505b92915050565b60165460ff16610bff5760405162461bcd60e51b81526004016109e490613a80565b610c0b83838333611f7d565b505050565b60165460ff16610c325760405162461bcd60e51b81526004016109e490613a80565b601654610100900460ff16600114610c4957600080fd5b6016805461ff001916610200179055610c623382612043565b610c6e57610c6e613af3565b6000818152600a6020526040902054158015610c9957506000818152600b602052604090205460ff16155b610cb55760405162461bcd60e51b81526004016109e490613b09565b60008181526003602090815260409182902082518084019093528054600f0b835260010154908201819052421015610d285760405162461bcd60e51b8152602060048201526016602482015275546865206c6f636b206469646e27742065787069726560501b60448201526064016109e4565b8051604080518082018252600080825260208083018281528783526003909152929020905181546001600160801b0319166001600160801b039091161781559051600190910155600254600f9190910b90610d838282613b41565b6002556040805180820190915260008082526020820152610da790859085906120a9565b60405163a9059cbb60e01b8152336004820152602481018390527f000000000000000000000000fd7c8060e57c5c78a582fa0dcd3d549db0a780f66001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610e14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e389190613b58565b610e4457610e44613af3565b610e4d846126c8565b60408051858152602081018490524281830152905133917f02f25270a4d87bea75db541cdfe559334a275b4a233520ed6c0a2429667cca94919081900360600190a27f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c81610ebb8482613b41565b6040805192835260208301919091520160405180910390a150506016805461ff0019166101001790555050565b60165460ff16610f0a5760405162461bcd60e51b81526004016109e490613a80565b610c0b83838360405180602001604052806000815250611836565b6000610f318383612043565b9392505050565b6000818152600860209081526040808320546007909252822081633b9aca008110610f6557610f65613add565b6003020154600160801b9004600f0b9392505050565b600c546001600160a01b03163314610f9257600080fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b600d8054610fc190613b75565b80601f0160208091040260200160405190810160405280929190818152602001828054610fed90613b75565b801561103a5780601f1061100f5761010080835404028352916020019161103a565b820191906000526020600020905b81548152906001019060200180831161101d57829003601f168201915b505050505081565b601654600090610100900460ff1660011461105c57600080fd5b6016805461ff001916610200179055611076838333612787565b90506016805461ff00191661010017905592915050565b6001600160a01b038116600090815260116020526040812054610bd7565b600554600081815260066020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015290919061110381856128ec565b949350505050565b6000546001600160a01b03163314806111a0575060015460405163b700961360e01b81526001600160a01b039091169063b70096139061115f90339030906001600160e01b03196000351690600401613bb0565b602060405180830381865afa15801561117c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111a09190613b58565b6111a957600080fd5b600180546001600160a01b0319166001600160a01b03831690811790915560405133907fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b7638998019890600090a350565b6000610f3183836129ee565b60004382111561121357611213613af3565b60055460006112228483612ccb565b600081815260066020908152604080832081516080810183528154600f81810b8352600160801b909104900b9381019390935260018101549183019190915260020154606082015291925083831015611331576000600681611285866001613bdd565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608083018290528501519192501461132b57826060015181606001516112f19190613b41565b836040015182604001516113059190613b41565b6060850151611314908a613b41565b61131e9190613bf5565b6113289190613c2a565b91505b50611380565b4382606001511461138057606082015161134b9043613b41565b604083015161135a9042613b41565b60608401516113699089613b41565b6113739190613bf5565b61137d9190613c2a565b90505b611399828284604001516113949190613bdd565b6128ec565b9695505050505050565b600c546001600160a01b031633146113ba57600080fd5b6000818152600a60205260409020546113d590600190613b41565b6000918252600a602052604090912055565b601654610100900460ff166001146113fe57600080fd5b6016805461ff0019166102001790556114173383612043565b61142357611423613af3565b60008281526003602090815260409182902082518084019093528054600f0b835260010154908201528115158061145d575060165460ff16155b61146957611469613af3565b60008160000151600f0b1380611482575060165460ff16155b6114c75760405162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b60448201526064016109e4565b428160200151116114ea5760405162461bcd60e51b81526004016109e490613c3e565b6114f983836000846002612d55565b50506016805461ff00191661010017905550565b60165460ff1661152f5760405162461bcd60e51b81526004016109e490613a80565b6001600160a01b03821633141561154857611548613af3565b3360008181526014602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b601654610100900460ff166001146115cb57600080fd5b6016805461ff0019166102001790556115e43383612043565b6115f0576115f0613af3565b600082815260036020908152604080832081518083019092528054600f0b825260010154918101919091529062093a808061162b8542613bdd565b6116359190613c2a565b61163f9190613bf5565b9050428260200151116116835760405162461bcd60e51b815260206004820152600c60248201526b131bd8dac8195e1c1a5c995960a21b60448201526064016109e4565b60008260000151600f0b136116ce5760405162461bcd60e51b8152602060048201526011602482015270139bdd1a1a5b99c81a5cc81b1bd8dad959607a1b60448201526064016109e4565b816020015181116117215760405162461bcd60e51b815260206004820152601f60248201527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e0060448201526064016109e4565b61172f6303c2670042613bdd565b81111561177e5760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652034207965617273206d6178000060448201526064016109e4565b61178d84600083856003612d55565b50506016805461ff0019166101001790555050565b6117b8336000356001600160e01b031916611ed4565b6117d45760405162461bcd60e51b81526004016109e490613ab7565b60165460ff16156118275760405162461bcd60e51b815260206004820152601760248201527f756e6c6f636b20616c72656164792068617070656e656400000000000000000060448201526064016109e4565b6016805460ff19166001179055565b60165460ff166118585760405162461bcd60e51b81526004016109e490613a80565b61186484848433611f7d565b823b1561197f57604051630a85bd0160e11b81526001600160a01b0384169063150b7a029061189d903390889087908790600401613c82565b6020604051808303816000875af19250505080156118d8575060408051601f3d908101601f191682019092526118d591810190613cb5565b60015b61197d573d808015611906576040519150601f19603f3d011682016040523d82523d6000602084013e61190b565b606091505b5080516119755760405162461bcd60e51b815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b60648201526084016109e4565b805181602001fd5b505b50505050565b600c546001600160a01b0316331461199c57600080fd5b6000908152600b60205260409020805460ff19169055565b6119f2600060405180604001604052806000600f0b8152602001600081525060405180604001604052806000600f0b815260200160008152506120a9565b565b6000818152600f60205260409020546060906001600160a01b0316611a5b5760405162461bcd60e51b815260206004820152601b60248201527f517565727920666f72206e6f6e6578697374656e7420746f6b656e000000000060448201526064016109e4565b60008281526003602090815260409182902082518084019093528054600f0b83526001015490820152610f3183611a928142612f5a565b83602001518460000151600f0b600d8054611aac90613b75565b80601f0160208091040260200160405190810160405280929190818152602001828054611ad890613b75565b8015611b255780601f10611afa57610100808354040283529160200191611b25565b820191906000526020600020905b815481529060010190602001808311611b0857829003601f168201915b505050505061302c565b611b45336000356001600160e01b031916611ed4565b611b615760405162461bcd60e51b81526004016109e490613ab7565b8051611b7490600d906020840190613669565b5050565b60165460ff16611b9a5760405162461bcd60e51b81526004016109e490613a80565b6000828152600a6020526040902054158015611bc557506000828152600b602052604090205460ff16155b611be15760405162461bcd60e51b81526004016109e490613b09565b80821415611bee57600080fd5b611bf83383612043565b611c0157600080fd5b611c0b3382612043565b611c1457600080fd5b6000828152600360208181526040808420815180830183528154600f90810b825260019283015482860190815288885295855283872084518086019095528054820b855290920154938301849052805194519095929490910b921115611c7e578260200151611c84565b83602001515b604080518082018252600080825260208083018281528b835260038252848320935184546001600160801b0319166001600160801b0390911617845551600190930192909255825180840190935280835290820152909150611ce990879086906120a9565b611cf2866126c8565b611d00858383866004612d55565b505050505050565b601654600090610100900460ff16600114611d2257600080fd5b6016805461ff001916610200179055611d3c848484612787565b90506016805461ff0019166101001790559392505050565b6000610f318383612f5a565b600081815260046020526040812054431415611d7e57506000919050565b610bd78242612f5a565b601654610100900460ff16600114611d9f57600080fd5b6016805461ff00191661020017905560008281526003602090815260409182902082518084019093528054600f0b8352600101549082015281611de157600080fd5b60008160000151600f0b1380611dfa575060165460ff16155b611e3f5760405162461bcd60e51b8152602060048201526016602482015275139bc8195e1a5cdd1a5b99c81b1bd8dac8199bdd5b9960521b60448201526064016109e4565b42816020015111611e625760405162461bcd60e51b81526004016109e490613c3e565b6114f983836000846000612d55565b600c546001600160a01b03163314611e8857600080fd5b6000818152600a60205260409020546113d5906001613bdd565b600c546001600160a01b03163314611eb957600080fd5b6000908152600b60205260409020805460ff19166001179055565b6001546000906001600160a01b03168015801590611f5e575060405163b700961360e01b81526001600160a01b0382169063b700961390611f1d90879030908890600401613bb0565b602060405180830381865afa158015611f3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f5e9190613b58565b8061110357506000546001600160a01b03858116911614949350505050565b6000828152600a6020526040902054158015611fa857506000828152600b602052604090205460ff16155b611fc45760405162461bcd60e51b81526004016109e490613b09565b611fce8183612043565b611fd757600080fd5b611fe18483613166565b611feb84836131cb565b611ff5838361324c565b6000828152600460205260408082204390555183916001600160a01b0380871692908816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a450505050565b6000818152600f602090815260408083205460108352818420546001600160a01b039182168086526014855283862088841680885295529285205492938085149392909116149060ff1682806120965750815b8061209e5750805b979650505050505050565b6040805160808101825260008082526020820181905291810182905260608101919091526040805160808101825260008082526020820181905291810182905260608101919091526005546000908190871561221557428760200151118015612119575060008760000151600f0b135b1561215e57865161212f906303c2670090613cd2565b600f0b602080870191909152870151612149904290613b41565b85602001516121589190613d10565b600f0b85525b428660200151118015612178575060008660000151600f0b135b156121bd57855161218e906303c2670090613cd2565b600f0b6020808601919091528601516121a8904290613b41565b84602001516121b79190613d10565b600f0b84525b602080880151600090815260098252604090205490870151600f9190910b935015612215578660200151866020015114156121fa57829150612215565b602080870151600090815260099091526040902054600f0b91505b60408051608081018252600080825260208201524291810191909152436060820152811561228a575060008181526006602090815260409182902082516080810184528154600f81810b8352600160801b909104900b9281019290925260018101549282019290925260029091015460608201525b6040810151816000428310156122d75760408401516122a99042613b41565b60608501516122b89043613b41565b6122ca90670de0b6b3a7640000613bf5565b6122d49190613c2a565b90505b600062093a806122e78186613c2a565b6122f19190613bf5565b905060005b60ff81101561246c5761230c62093a8083613bdd565b915060004283111561232057429250612334565b50600082815260096020526040902054600f0b5b61233e8684613b41565b876020015161234d9190613d10565b8751889061235c908390613da5565b600f0b905250602087018051829190612376908390613df5565b600f90810b90915288516000910b1215905061239157600087525b60008760200151600f0b12156123a957600060208801525b60408088018490528501519295508592670de0b6b3a7640000906123cd9085613b41565b6123d79086613bf5565b6123e19190613c2a565b85606001516123f09190613bdd565b6060880152612400600189613bdd565b975042831415612416575043606087015261246c565b6000888152600660209081526040918290208951918a01516001600160801b03908116600160801b02921691909117815590880151600182015560608801516002909101555061246581613e44565b90506122f6565b505060058590558b156124f7578860200151886020015161248d9190613da5565b8460200181815161249e9190613df5565b600f0b905250885188516124b29190613da5565b845185906124c1908390613df5565b600f90810b90915260208601516000910b121590506124e257600060208501525b60008460000151600f0b12156124f757600084525b6000858152600660209081526040918290208651918701516001600160801b03908116600160801b02921691909117815590850151600182015560608501516002909101558b156126ba57428b6020015111156125af57602089015161255d9088613df5565b96508a602001518a60200151141561258157602088015161257e9088613da5565b96505b60208b810151600090815260099091526040902080546001600160801b0319166001600160801b0389161790555b428a60200151111561260a578a602001518a60200151111561260a5760208801516125da9087613da5565b60208b810151600090815260099091526040902080546001600160801b0319166001600160801b03831617905595505b60008c815260086020526040812054612624906001613bdd565b905080600860008f815260200190815260200160002081905550428960400181815250504389606001818152505088600760008f815260200190815260200160002082633b9aca00811061267a5761267a613add565b825160208401516001600160801b03908116600160801b029116176003919091029190910190815560408201516001820155606090910151600290910155505b505050505050505050505050565b6126d23382612043565b61271e5760405162461bcd60e51b815260206004820181905260248201527f63616c6c6572206973206e6f74206f776e6572206e6f7220617070726f76656460448201526064016109e4565b6000818152600f60205260408120546001600160a01b03169061274190836109c2565b61274b33836131cb565b60405182906000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b60008062093a80806127998642613bdd565b6127a39190613c2a565b6127ad9190613bf5565b905060008511806127c1575060165460ff16155b6127ca57600080fd5b4281116128285760405162461bcd60e51b815260206004820152602660248201527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e207468652060448201526566757475726560d01b60648201526084016109e4565b6128366303c2670042613bdd565b8111156128855760405162461bcd60e51b815260206004820152601e60248201527f566f74696e67206c6f636b2063616e2062652032207965617273206d6178000060448201526064016109e4565b600e6000815461289490613e44565b90915550600e546128a584826132e2565b5060008181526003602090815260409182902082518084019093528054600f0b8352600190810154918301919091526128e391839189918691612d55565b95945050505050565b600080839050600062093a808083604001516129089190613c2a565b6129129190613bf5565b905060005b60ff8110156129c65761292d62093a8083613bdd565b915060008583111561294157859250612955565b50600082815260096020526040902054600f0b5b60408401516129649084613b41565b84602001516129739190613d10565b84518590612982908390613da5565b600f0b9052508286141561299657506129c6565b80846020018181516129a89190613df5565b600f0b90525050604083018290526129bf81613e44565b9050612917565b5060008260000151600f0b12156129dc57600082525b50516001600160801b03169392505050565b600043821115612a0057612a00613af3565b600083815260086020526040812054815b6080811015612aa457818310612a2657612aa4565b60006002612a348486613bdd565b612a3f906001613bdd565b612a499190613c2a565b6000888152600760205260409020909150869082633b9aca008110612a7057612a70613add565b600302016002015411612a8557809350612a93565b612a90600182613b41565b92505b50612a9d81613e44565b9050612a11565b50600085815260076020526040812083633b9aca008110612ac757612ac7613add565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b602083015260018101549282019290925260029091015460608201526005549091506000612b1c8783612ccb565b600081815260066020908152604080832081516080810183528154600f81810b8352600160801b909104900b938101939093526001810154918301919091526002015460608201529192508084841015612bfb576000600681612b80876001613bdd565b8152602080820192909252604090810160002081516080810183528154600f81810b8352600160801b909104900b93810193909352600181015491830191909152600201546060808301829052860151919250612bdd9190613b41565b925083604001518160400151612bf39190613b41565b915050612c1f565b6060830151612c0a9043613b41565b9150826040015142612c1c9190613b41565b90505b60408301518215612c5c578284606001518c612c3b9190613b41565b612c459084613bf5565b612c4f9190613c2a565b612c599082613bdd565b90505b6040870151612c6b9082613b41565b8760200151612c7a9190613d10565b87518890612c89908390613da5565b600f90810b90915288516000910b129050612cb957505093516001600160801b03169650610bd795505050505050565b60009950505050505050505050610bd7565b60008082815b6080811015612d4b57818310612ce657612d4b565b60006002612cf48486613bdd565b612cff906001613bdd565b612d099190613c2a565b6000818152600660205260409020600201549091508710612d2c57809350612d3a565b612d37600182613b41565b92505b50612d4481613e44565b9050612cd1565b5090949350505050565b6002548290612d648682613bdd565b6002556040805180820190915260008082526020820152825160208085015190830152600f0b8152825187908490612d9d908390613df5565b600f0b9052508515612db157602083018690525b6000888152600360209081526040909120845181546001600160801b0319166001600160801b0390911617815590840151600190910155612df38882856120a9565b338715801590612e1557506004856004811115612e1257612e12613e5f565b14155b15612ebf576040516323b872dd60e01b81526001600160a01b038281166004830152306024830152604482018a90527f000000000000000000000000fd7c8060e57c5c78a582fa0dcd3d549db0a780f616906323b872dd906064016020604051808303816000875af1158015612e8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eb39190613b58565b612ebf57612ebf613af3565b8360200151816001600160a01b03167fff04ccafc360e16b67d682d17bd9503c4c6b9a131f6be6325762dc9ffc7de6248b8b8942604051612f039493929190613e75565b60405180910390a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c83612f378a82613bdd565b6040805192835260208301919091520160405180910390a1505050505050505050565b60008281526008602052604081205480612f78576000915050610bd7565b600084815260076020526040812082633b9aca008110612f9a57612f9a613add565b60408051608081018252600392909202929092018054600f81810b8452600160801b909104900b602083015260018101549282018390526002015460608201529150612fe69085613eb3565b8160200151612ff59190613d10565b81518290613004908390613da5565b600f90810b90915282516000910b1215905061301f57600081525b51600f0b9150610bd79050565b606060405180610120016040528060fd81526020016141ee60fd913990508061305487613345565b604051602001613065929190613ef2565b60405160208183030381529060405290508061308086613345565b604051602001613091929190613f6e565b6040516020818303038152906040529050806130ac85613345565b6040516020016130bd929190613fee565b6040516020818303038152906040529050806130d884613345565b6040516020016130e992919061406f565b6040516020818303038152906040529050600061313861310888613345565b8461311285613443565b604051602001613124939291906140ca565b604051602081830303815290604052613443565b90508060405160200161314b9190614194565b60405160208183030381529060405291505095945050505050565b6000818152600f60205260409020546001600160a01b0383811691161461318f5761318f613af3565b6000818152601060205260409020546001600160a01b031615611b7457600090815260106020526040902080546001600160a01b031916905550565b6000818152600f60205260409020546001600160a01b038381169116146131f4576131f4613af3565b6000818152600f6020526040902080546001600160a01b031916905561321a82826135a9565b6001600160a01b0382166000908152601160205260408120805460019290613243908490613b41565b90915550505050565b6000818152600f60205260409020546001600160a01b03161561327157613271613af3565b6000818152600f6020908152604080832080546001600160a01b0319166001600160a01b038716908117909155808452601180845282852080546012865284872081885286528487208890558787526013865293862093909355908452909152805460019290613243908490613bdd565b60006001600160a01b0383166132fa576132fa613af3565b613304838361324c565b60405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a450600192915050565b6060816133695750506040805180820190915260018152600360fc1b602082015290565b8160005b8115613393578061337d81613e44565b915061338c9050600a83613c2a565b915061336d565b60008167ffffffffffffffff8111156133ae576133ae6138c4565b6040519080825280601f01601f1916602001820160405280156133d8576020820181803683370190505b5090505b8415611103576133ed600183613b41565b91506133fa600a866141d9565b613405906030613bdd565b60f81b81838151811061341a5761341a613add565b60200101906001600160f81b031916908160001a90535061343c600a86613c2a565b94506133dc565b805160609080613463575050604080516020810190915260008152919050565b60006003613472836002613bdd565b61347c9190613c2a565b613487906004613bf5565b90506000613496826020613bdd565b67ffffffffffffffff8111156134ae576134ae6138c4565b6040519080825280601f01601f1916602001820160405280156134d8576020820181803683370190505b50905060006040518060600160405280604081526020016142eb604091399050600181016020830160005b86811015613564576003818a01810151603f601282901c8116860151600c83901c8216870151600684901c831688015192909316870151600891821b60ff94851601821b92841692909201901b91160160e01b835260049092019101613503565b50600386066001811461357e576002811461358f5761359b565b613d3d60f01b60011983015261359b565b603d60f81b6000198301525b505050918152949350505050565b6001600160a01b0382166000908152601160205260408120546135ce90600190613b41565b6000838152601360205260409020549091508082141561361e576001600160a01b03841660009081526012602090815260408083208584528252808320839055858352601390915281205561197f565b6001600160a01b039390931660009081526012602090815260408083209383529281528282208054868452848420819055835260139091528282209490945592839055908252812055565b82805461367590613b75565b90600052602060002090601f01602090048101928261369757600085556136dd565b82601f106136b057805160ff19168380011785556136dd565b828001600101855582156136dd579182015b828111156136dd5782518255916020019190600101906136c2565b506136e99291506136ed565b5090565b5b808211156136e957600081556001016136ee565b6001600160e01b03198116811461371857600080fd5b50565b60006020828403121561372d57600080fd5b8135610f3181613702565b60005b8381101561375357818101518382015260200161373b565b8381111561197f5750506000910152565b6000815180845261377c816020860160208601613738565b601f01601f19169290920160200192915050565b602081526000610f316020830184613764565b6000602082840312156137b557600080fd5b5035919050565b6001600160a01b038116811461371857600080fd5b600080604083850312156137e457600080fd5b82356137ef816137bc565b946020939093013593505050565b6000806040838503121561381057600080fd5b50508035926020909101359150565b60006020828403121561383157600080fd5b8135610f31816137bc565b60008060006060848603121561385157600080fd5b833561385c816137bc565b9250602084013561386c816137bc565b929592945050506040919091013590565b801515811461371857600080fd5b6000806040838503121561389e57600080fd5b82356138a9816137bc565b915060208301356138b98161387d565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff808411156138f5576138f56138c4565b604051601f8501601f19908116603f0116810190828211818310171561391d5761391d6138c4565b8160405280935085815286868601111561393657600080fd5b858560208301376000602087830101525050509392505050565b6000806000806080858703121561396657600080fd5b8435613971816137bc565b93506020850135613981816137bc565b925060408501359150606085013567ffffffffffffffff8111156139a457600080fd5b8501601f810187136139b557600080fd5b6139c4878235602084016138da565b91505092959194509250565b6000602082840312156139e257600080fd5b813567ffffffffffffffff8111156139f957600080fd5b8201601f81018413613a0a57600080fd5b611103848235602084016138da565b600080600060608486031215613a2e57600080fd5b83359250602084013591506040840135613a47816137bc565b809150509250925092565b60008060408385031215613a6557600080fd5b8235613a70816137bc565b915060208301356138b9816137bc565b60208082526019908201527f636f6e7472616374206d75737420626520756e6c6f636b656400000000000000604082015260600190565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b602080825260089082015267185d1d1858da195960c21b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082821015613b5357613b53613b2b565b500390565b600060208284031215613b6a57600080fd5b8151610f318161387d565b600181811c90821680613b8957607f821691505b60208210811415613baa57634e487b7160e01b600052602260045260246000fd5b50919050565b6001600160a01b0393841681529190921660208201526001600160e01b0319909116604082015260600190565b60008219821115613bf057613bf0613b2b565b500190565b6000816000190483118215151615613c0f57613c0f613b2b565b500290565b634e487b7160e01b600052601260045260246000fd5b600082613c3957613c39613c14565b500490565b60208082526024908201527f43616e6e6f742061646420746f2065787069726564206c6f636b2e20576974686040820152636472617760e01b606082015260800190565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061139990830184613764565b600060208284031215613cc757600080fd5b8151610f3181613702565b600081600f0b83600f0b80613ce957613ce9613c14565b60016001607f1b0319821460001982141615613d0757613d07613b2b565b90059392505050565b600081600f0b83600f0b60016001607f1b03600082136000841383830485118282161615613d4057613d40613b2b565b60016001607f1b03196000851282811687830587121615613d6357613d63613b2b565b60008712925085820587128484161615613d7f57613d7f613b2b565b85850587128184161615613d9557613d95613b2b565b5050509290910295945050505050565b600081600f0b83600f0b600081128160016001607f1b031901831281151615613dd057613dd0613b2b565b8160016001607f1b03018313811615613deb57613deb613b2b565b5090039392505050565b600081600f0b83600f0b600082128260016001607f1b0303821381151615613e1f57613e1f613b2b565b8260016001607f1b0319038212811615613e3b57613e3b613b2b565b50019392505050565b6000600019821415613e5857613e58613b2b565b5060010190565b634e487b7160e01b600052602160045260246000fd5b848152602081018490526080810160058410613ea157634e487b7160e01b600052602160045260246000fd5b60408201939093526060015292915050565b60008083128015600160ff1b850184121615613ed157613ed1613b2b565b6001600160ff1b0384018313811615613eec57613eec613b2b565b50500390565b60008351613f04818460208801613738565b6503a37b5b2b7160d51b9083019081528351613f27816006840160208801613738565b7f3c2f746578743e3c7465787420783d2231302220793d2234302220636c61737360069290910191820152671e913130b9b2911f60c11b6026820152602e01949350505050565b60008351613f80818460208801613738565b6903130b630b731b2a7b3160b51b9083019081528351613fa781600a840160208801613738565b7f3c2f746578743e3c7465787420783d2231302220793d2236302220636c617373600a9290910191820152671e913130b9b2911f60c11b602a820152603201949350505050565b60008351614000818460208801613738565b6a03637b1b5b2b22fb2b732160ad1b908301908152835161402881600b840160208801613738565b7f3c2f746578743e3c7465787420783d2231302220793d2238302220636c617373600b9290910191820152671e913130b9b2911f60c11b602b820152603301949350505050565b60008351614081818460208801613738565b6503b30b63ab2960d51b90830190815283516140a4816006840160208801613738565b6c1e17ba32bc3a1f1e17b9bb339f60991b60069290910191820152601301949350505050565b707b226e616d65223a20224261646765202360781b815283516000906140f7816011850160208901613738565b72111610113232b9b1b934b83a34b7b7111d101160691b601191840191820152845161412a816024840160208901613738565b7f222c2022696d616765223a2022646174613a696d6167652f7376672b786d6c3b602492909101918201526618985cd94d8d0b60ca1b6044820152835161417881604b840160208801613738565b61227d60f01b604b9290910191820152604d0195945050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516141cc81601d850160208701613738565b91909101601d0192915050565b6000826141e8576141e8613c14565b50069056fe3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f73766722207072657365727665417370656374526174696f3d22784d696e594d696e206d656574222076696577426f783d223020302033353020333530223e3c7374796c653e2e62617365207b2066696c6c3a2077686974653b20666f6e742d66616d696c793a2073657269663b20666f6e742d73697a653a20313470783b207d3c2f7374796c653e3c726563742077696474683d223130302522206865696768743d2231303025222066696c6c3d22626c61636b22202f3e3c7465787420783d2231302220793d2232302220636c6173733d2262617365223e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa264697066735822122067c5a9ff4136c7ab55ac4f81e85ab66a538b3e1b8b701bb048164a97fc26971564736f6c634300080b0033

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

000000000000000000000000fd7c8060e57c5c78a582fa0dcd3d549db0a780f60000000000000000000000004d03fb78bda67a04f1bd6fde5024759d8ce8d866000000000000000000000000cbb46b017e8d785c107e97c56135894b3ead599c

-----Decoded View---------------
Arg [0] : TOKEN_ADDR_ (address): 0xfd7C8060E57C5c78a582FA0DcD3d549db0a780F6
Arg [1] : GOVERNANCE_ (address): 0x4d03Fb78BdA67a04f1bD6fDE5024759D8Ce8D866
Arg [2] : AUTHORITY_ (address): 0xcbb46b017e8d785C107e97c56135894b3eAD599C

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000fd7c8060e57c5c78a582fa0dcd3d549db0a780f6
Arg [1] : 0000000000000000000000004d03fb78bda67a04f1bd6fde5024759d8ce8d866
Arg [2] : 000000000000000000000000cbb46b017e8d785c107e97c56135894b3ead599c


Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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