ETH Price: $3,122.37 (+0.06%)

Token

Cypher (CPHR)
 

Overview

Max Total Supply

1,024 CPHR

Holders

498

Market

Volume (24H)

N/A

Min Price (24H)

N/A

Max Price (24H)

N/A
Filtered by Token Holder
kentaro.eth
Balance
1 CPHR
0x6745428660b2C154af3a8309358D0C92634d7461
Loading...
Loading
Loading...
Loading
Loading...
Loading

OVERVIEW

Cypher is a generative art project by Hideki Tsukamoto comprised of 1024 tokens calculated and drawn via smart contract, by the Ethereum Virtual Machine. Cypher is part one of the 'Apex' series.

# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
Cypher

Compiler Version
v0.8.6+commit.11564f7e

Optimization Enabled:
Yes with 0 runs

Other Settings:
default evmVersion, None license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2022-11-09
*/

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0 <0.9.0;

// ===============================================
//          TERMS AND CONDITIONS
//        https://www.anma.io/legal
// ===============================================

/*
    HIDEKI TSUKAMOTO | ANOMALOUS MATERIALS | 15.08.2021
______________.___.__________  ___ _______________________ 
\_   ___ \__  |   |\______   \/   |   \_   _____|______   \
/    \  \//   |   | |     ___/    ~    \    __)_ |       _/
\     \___\____   | |    |   \    Y    /        \|    |   \
 \______  / ______| |____|    \___|_  /_______  /|____|_  /
        \/\/                        \/        \/        \/ 
*/

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}


/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _setOwner(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

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

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

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}
/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

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

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

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) private pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}


/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}



/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overriden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        require(operator != _msgSender(), "ERC721: approve to caller");

        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /**
     * @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.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, _data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

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

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits a {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721Receiver(to).onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}


/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
 * enumerability of all the token ids in the contract as well as all token ids owned by each
 * account.
 */
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
    // Mapping from owner to list of owned token IDs
    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

    // Mapping from token ID to index of the owner tokens list
    mapping(uint256 => uint256) private _ownedTokensIndex;

    // Array with all token ids, used for enumeration
    uint256[] private _allTokens;

    // Mapping from token id to position in the allTokens array
    mapping(uint256 => uint256) private _allTokensIndex;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
        return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _allTokens.length;
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, tokenId);

        if (from == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (from != to) {
            _removeTokenFromOwnerEnumeration(from, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (to != from) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = ERC721.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }
}

struct CypherAttributes
{
    uint colorset;
    int decay;
    int chaos;
    int utilRand;
    int numChannels;
    int[8] density;
    int[8] intricacy;
}


struct StringBuilder
{
    bytes data;
}

library SB
{
    function create(uint256 capacity)
        internal pure returns(StringBuilder memory)
    {
        return StringBuilder(new bytes(capacity + 32));
    }

    function resize(StringBuilder memory sb, uint256 newCapacity) 
        internal view
    {
        StringBuilder memory newSb = create(newCapacity);
        
        assembly 
        {
            let data := mload(sb)
            let newData := mload(newSb)
            let size := mload(add(data, 32)) // get used byte count
            let bytesToCopy := add(size, 32) // copy the used bytes, plus the size field in first 32 bytes
            
            pop(staticcall(
                gas(), 
                0x4, 
                add(data, 32), 
                bytesToCopy, 
                add(newData, 32), 
                bytesToCopy))
        }
        
        sb.data = newSb.data;
    }

    function resizeIfNeeded(StringBuilder memory sb, uint256 spaceNeeded) 
        internal view
    {
        uint capacity;
        uint size;
        assembly
        {
            let data := mload(sb)
            capacity := sub(mload(data), 32)
            size := mload(add(data, 32))
        }

        uint remaining = capacity - size;
        if (remaining >= spaceNeeded)
        {
            return;
        }

        uint newCapacity = capacity << 1;
        uint newRemaining = newCapacity - size;
        if (newRemaining >= spaceNeeded)
        {
            resize(sb, newCapacity);
        }
        else
        {
            newCapacity = spaceNeeded + size;
            resize(sb, newCapacity);
        }
    }
    
    function getString(StringBuilder memory sb) 
        internal pure returns(string memory) 
    {
        string memory ret;
        assembly 
        {
            let data := mload(sb)
            ret := add(data, 32)
        }
        return ret;
    }

    function writeStr(StringBuilder memory sb, string memory str) 
        internal view
    {
        resizeIfNeeded(sb, bytes(str).length);

        assembly 
        {
            let data := mload(sb)
            let size := mload(add(data, 32))
            pop(staticcall(gas(), 0x4, add(str, 32), mload(str), add(size, add(data, 64)), mload(str)))
            mstore(add(data, 32), add(size, mload(str)))
        }
    }

    function concat(StringBuilder memory dst, StringBuilder memory src) 
        internal view
    {
        string memory asString;
        assembly
        {
            let srcData := mload(src)
            asString := add(srcData, 32)
        }

        writeStr(dst, asString);
    }

    function writeUint(StringBuilder memory sb, uint u) 
        internal view
    {
        if (u > 0)
        {
            uint len;
            uint size;
            
            assembly
            {
                // get length string will be
                len := 0
                
                for {let val := u} gt(val, 0) {val := div(val,  10) len := add(len, 1)}
                {
                }

                // get bytes currently used
                let data := mload(sb)
                size := mload(add(data, 32))
            }
            
            // make sure there's room
            resizeIfNeeded(sb, len);
            
            assembly
            {
                let data := mload(sb)

                for {let i := 0 let val := u} lt(i, len) {i := add(i, 1) val := div(val, 10)}
                {
                    // sb.data[64 + size + (len - i - 1)] = (val % 10) + 48
                    mstore8(add(data, add(63, add(size, sub(len, i)))), add(mod(val, 10), 48))
                }
            
                size := add(size, len)
            
                mstore(add(data, 32), size)
            }
        }
        else
        {
            uint size;
            assembly
            {
                let data := mload(sb)
                size := mload(add(data, 32))
            }
            // make sure there's room
            resizeIfNeeded(sb, 1);
            
            assembly
            {
                let data := mload(sb)
                mstore(add(data, 32), add(size, 1))
                mstore8(add(data, add(64, size)), 48)
            }
        }
    }
    
    function writeInt(StringBuilder memory sb, int i) 
        internal view
    {
        if (i < 0)
        {
            // write the - sign
            uint size;
            assembly
            {
                let data := mload(sb)
                size := mload(add(data, 32))
            }
            resizeIfNeeded(sb, 1);
            
            assembly
            {
                let data := mload(sb)
                mstore(add(data, 32), add(size, 1))
                mstore8(add(data, add(64, size)), 45)
            }

            // now the digits can be written as a uint
            i *= -1;
        }
        writeUint(sb, uint(i));
    }

    function writeRgb(StringBuilder memory sb, uint256 col) 
        internal view
    {
        resizeIfNeeded(sb, 6);

        string[16] memory nibbles = [
            "0", "1", "2", "3", "4", "5", "6", "7", 
            "8", "9", "a", "b", "c", "d", "e", "f"];

        string memory asStr = string(abi.encodePacked(
            nibbles[(col >> 20) & 0xf],
            nibbles[(col >> 16) & 0xf],
            nibbles[(col >> 12) & 0xf],
            nibbles[(col >> 8) & 0xf],
            nibbles[(col >> 4) & 0xf],
            nibbles[col & 0xf]
        ));

        writeStr(sb, asStr);
    }
}


struct Rand
{
    uint256 value;
}

library Random
{
    function create(uint256 srand) 
        internal pure returns(Rand memory) 
    {
        Rand memory rand = Rand({value: srand});
        return rand;
    }
    
    function value(Rand memory rand) 
        internal pure returns(uint256) 
    {
        rand.value = uint256(keccak256(abi.encodePacked(rand.value)));
        return rand.value;
    }
    
    // (max inclusive)
    function range(Rand memory rand, int256 min, int256 max) 
        internal pure returns(int256) 
    {
        if (min <= max)
        {
            uint256 span = uint256(max - min);

            return int256(value(rand) % (span + 1)) + min;
        }
        else
        {
            return range(rand, max, min);
        }
    }
}

contract CypherDrawing is Ownable
{   
    int constant FONT_SIZE = 4;

    uint8[1024] private curve;
    int8[1024] private noiseTable;
    uint24[256][5] private gradients;

    function setCurve(uint8[1024] memory newCurve) 
        public onlyOwner
    {
        curve = newCurve;
    }

    function setNoiseTable(int8[1024] memory newNoiseTable) 
        public onlyOwner
    {
        noiseTable = newNoiseTable;
    }

    function setGradients(uint24[256][5] memory newGradients)
        public onlyOwner
    {
        gradients = newGradients;
    }

    function getAttributes(bytes32 hash)
        public view returns (CypherAttributes memory)
    {
        Rand memory rand = Random.create(uint256(hash));
        CypherAttributes memory attributes = createAttributes(rand);
        return attributes;
    }

    function generate(bytes32 hash) 
        public view returns(string memory)
    {
        StringBuilder memory b = SB.create(128 * 1024);

        SB.writeStr(b, "<svg viewBox='0 0 640 640' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>"
            
            "<style>"
            "text{"
                "font-size:");
        SB.writeInt(b, FONT_SIZE);
        SB.writeStr(b, "px;"
                "font-family: monospace;"
                "fill: #cccccc;"
            "}"
            "</style>"

            "<defs>"
                "<filter id='glow'>"
                    "<feGaussianBlur stdDeviation='3.0' result='coloredBlur'/>"
                    "<feComponentTransfer in='coloredBlur' result='coloredBlur'>"
                        "<feFuncA type='linear' slope='0.70'/>"
                    "</feComponentTransfer>"
                    "<feMerge>"
                        "<feMergeNode in='coloredBlur'/>"
                        "<feMergeNode in='SourceGraphic'/>"
                    "</feMerge>"
                "</filter>"
            "</defs>"

            "<rect width='640' height='640' fill='#090809'/>"              
            
            "<g id='cypher' shape-rendering='geometricPrecision' filter='url(#glow)'>");
        
        Rand memory rand = Random.create(uint256(hash));
        CypherAttributes memory attributes = createAttributes(rand);
        draw(b, attributes, rand);
            
        SB.writeStr(b, "</g>"
            
            "</svg>");

        return SB.getString(b);
    }

    struct Ring
    {
        uint id;
        int arcs;
        int span;
        int inner;
        int outer;
    }

    struct SegmentData
    {
        uint ringId;
        uint segId;
        int inner;
        int outer;
        int thick;
        int start;
        int fin;
        EdgeType edge;
        FillType fill;
        SpanType innerSpanType;
        PadType padInner;
        SpanType outerSpanType;
        PadType padOuter;
        uint colour;
    }

    enum SpanType
    {
        None,
        Arc,
        Cap,
        Ang,
        Brk,
        Dotted
    }

    enum Variant
    {
        None,
        Inner,
        Outer,
        Double,
        Max
    }

    enum EdgeType
    {
        None,
        Simple
    }

    enum FillType
    {
        None,
        Block,
        Hollow,
        Text,
        Increment, 
        Comp
    }

    enum PadType
    {
        None,
        Single
    }

    
    function clamp(int num)
        private pure returns(int)
    {
        return clamp(num, 0, 31);
    }
    
    function clamp(int num, int min, int max)
        private pure returns(int)
    {
        return num <= min ? min : num >= max ? max : num;
    }

    function noise(Rand memory rand) 
        private view returns(int)
    {
        return noiseTable[uint(Random.range(rand, 0, 1023))];
    }

    function createAttributes(Rand memory rand) 
        private view returns(CypherAttributes memory)
    {
        int weighted = int8(curve[uint(Random.range(rand, 0, 1023))]);
        
        CypherAttributes memory attributes;
        attributes.colorset = (uint(weighted < int(8) ? int(8) : weighted) - 8)/6;
        attributes.decay = 32 - clamp(weighted + noise(rand));
        attributes.chaos = 32 - clamp(weighted + noise(rand));
        attributes.utilRand = Random.range(rand, 0, 1023);
        attributes.numChannels = 4 + (clamp(weighted + noise(rand), 0, 32) >> 3);
        
        int count = 0;
        while(true)
        {
            uint idx = uint(Random.range(rand, 0, 7));

            if(count == attributes.numChannels)
            { 
                break;
            }
            else if(attributes.density[idx]==1)
            {
                continue;
            } 
            else 
            {
                attributes.density[idx]=1;
                count++;
            }
        }

        for (uint i = 0; i < 8; ++i)
        {
            attributes.intricacy[i] = clamp(weighted + noise(rand));
        }

        return attributes;
    }

    function getSegmentColour(
        CypherAttributes memory atr, 
        Ring memory ring, 
        Rand memory rand) 
        private view returns(uint24)
    {
        int array_offset    = atr.utilRand % 256;
        int grad_noise      = Random.range(rand, 0, 30);
        int colour_index    = (array_offset + ring.inner + grad_noise) % 256;
        return gradients[atr.colorset][uint(colour_index)];
    }

    function draw(
        StringBuilder memory b, 
        CypherAttributes memory atr, 
        Rand memory rand) 
        private view
    {
        Ring[16] memory rings = createRings(rand);

        // frame
        for(uint i=0; i<16; ++i)
        {
            SB.writeStr(b, "<circle cx='320' cy='320' fill='none' stroke-width='0.1' stroke-opacity='15%' stroke='#");
            SB.writeRgb(b, gradients[atr.colorset][uint(rings[i].inner)-1]);
            SB.writeStr(b, "' r='");
            SB.writeInt(b, rings[i].inner);
            SB.writeStr(b, "'/>");
        }

        // defs & ring
        // defs added as we go, ring must be deferred
        SB.writeStr(b, "<defs>");
        StringBuilder memory ringSvg = SB.create(4096);
        for(uint i=0; i<16; i++)
        {
            uint channelIndex = (i >> 1);
            if(atr.density[channelIndex] == 0) continue;

            int span = rings[i].span;

            uint segs = 8 >> uint(Random.range(rand, 1, 2));

            int[] memory sections = new int[](segs);
            
            for(uint g=0; g<segs; g++)
            {
                sections[g] = 1;
            }

            {
                int increments = int(span)-int(segs);
                for(int s=0; s<increments; s++)
                {  
                    sections[uint(Random.range(rand, 0, int(segs) - 1))]++;
                }
            }

            int progress = int(span);

            // template
            SB.writeStr(b, "<g id='variant_r");
            SB.writeUint(b, rings[i].id);
            SB.writeStr(b, "_v0'>");
            for(uint t=0; t<segs; t++)
            {
                progress -= int(sections[t]); // TODO make sure everything with subtractions happens with ints

                SegmentData memory segmentData;
                segmentData.ringId = i;
                segmentData.segId = t;
                segmentData.inner = rings[i].inner;
                segmentData.outer = rings[i].outer;
                segmentData.thick = rings[i].outer - int(rings[i].inner);
                segmentData.start = progress * 5;
                segmentData.fin = sections[t] * 5;
                segmentData.edge = EdgeType(rings[i].inner % 2);
                {
                    int maxIntricacy = atr.intricacy[channelIndex] >> 3;
                    segmentData.fill = FillType(Random.range(rand, 2, 2+maxIntricacy));
                    segmentData.innerSpanType = SpanType(Random.range(rand, 2, 2+maxIntricacy));
                    segmentData.outerSpanType = SpanType(Random.range(rand, 2, 2+maxIntricacy));
                }
                segmentData.padInner = PadType(rings[i].outer % 2);
                segmentData.padOuter = PadType(rings[i].outer % 2);
                segmentData.colour = getSegmentColour(atr, rings[i], rand);

                if (Random.range(rand, 0, 10) > 7)
                {
                    segmentData.colour = (segmentData.colour & 0xfefefe) >> 1;
                }

                drawSegment(
                    b, 
                    segmentData,
                    rand);
            }
            SB.writeStr(b, "</g>");

            // arc
            SB.writeStr(ringSvg, "<g id='r");
            SB.writeUint(ringSvg, i);
            SB.writeStr(ringSvg, "'>");
            for (uint j = 0; j < uint(rings[i].arcs); j++)
            {            
                if (Random.range(rand, 0, 64) < atr.decay)
                {
                    continue;  //THIS HAS THE EFFECT I WAS LOOKING FOR.
                }
                
                int chaosAddition = Random.range(rand, 0, 720);

                int angle = atr.chaos < Random.range(rand, 0, 64) ? rings[i].span : chaosAddition;

                int rotation = (angle * int(j)) * 5;

                SB.writeStr(ringSvg, "<g id='r");
                SB.writeUint(ringSvg, i);
                SB.writeStr(ringSvg, "a");
                SB.writeUint(ringSvg, j);
                SB.writeStr(ringSvg, "' transform='rotate(");
                SB.writeInt(ringSvg, rotation);
                SB.writeStr(ringSvg, " 320 320)'><use xlink:href='#variant_r");
                SB.writeUint(ringSvg, i);
                SB.writeStr(ringSvg, "_v0'/> </g>");
            }
            
            uint shifted = 8 << uint(Random.range(rand, 0, 4));

            SB.writeStr(ringSvg, "<animateTransform attributeName='transform' attributeType='XML' type='rotate' from='0 320 320' to='");
            if (Random.range(rand, 0, 10) > 8)
            {
                SB.writeStr(ringSvg, "-");
            }
            SB.writeStr(ringSvg, "360 320 320' dur='");
            SB.writeUint(ringSvg, shifted);
            SB.writeStr(ringSvg, "s' begin='1s' repeatCount='indefinite'/></g>");
        }

        SB.writeStr(b, "</defs>");
        SB.concat(b, ringSvg);
    }

    function createRings(Rand memory rand) 
        private pure returns(Ring[16] memory)
    {
        uint8[8] memory chf = [0, 0, 0, 0, 0, 0, 0, 0];

        for(uint i=0; i<24; i++)
        {
            chf[uint(Random.range(rand, 0, 7))]++;
        }

        int[16] memory radii = [int(5), 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5];

        for(uint j=0; j<chf.length; j++) 
        {
            int total = int8(chf[j]);

            for(int i=0; i<total; i++) 
            {
                uint lower = j*2;
                uint upper = (j*2)+3;
                uint index = uint(Random.range(rand, int(lower), int(upper)));

                int adv = Random.range(rand, i, total);

                radii[index % 16]+=(adv*5);

                total-=adv;
            }
        }

        Ring[16] memory rings;

        uint8[5] memory increments =  [12, 18, 24, 36, 72];
        int progress = 60;

        for(uint i=0; i<16; i++)
        {
            uint idxInc            = uint(Random.range(rand, 0, int(increments.length)-1));
            int increment           = int8(increments[idxInc]);
            int pad                 = 1;
            int thisRingThickness = radii[i];

            
            int innerRadius        = progress+pad;
            int outerRadius        = int(innerRadius+thisRingThickness) - int(pad);

            progress += thisRingThickness;

            int numArcs  = 72/increment;
        
            rings[i] = Ring(
            {
                id          : i,
                arcs        : numArcs,
                span        : increment,
                inner       : innerRadius,
                outer       : outerRadius
            });
        }

        return rings;
    }

    function drawSpan(
        StringBuilder memory b,
        SpanType spanType, 
        int start, 
        int fin, 
        int radius, 
        PadType pad, 
        Variant variant,
        uint col,
        Rand memory rand) 
        private view
    {
        if (spanType == SpanType.Arc)
        {
            arc(b, start, fin, radius, pad, col, rand);
        }
        else if (spanType == SpanType.Dotted)
        {
            dotted(b, start, fin, radius, pad, col, rand);
        }
        else if (spanType == SpanType.Cap)
        {
            cap(b, start, fin, radius, pad, col, rand);
        }
        else if (spanType == SpanType.Ang)
        {
            ang(b, start, fin, radius, pad, variant, col, rand);
        }
        else if (spanType == SpanType.Brk)
        {
            brk(b, start, fin, radius, pad, variant, col, rand);
        }
    }

    function drawSegment(
        StringBuilder memory b, 
        SegmentData memory segmentData,
        Rand memory rand) 
        private view
    {
        SB.writeStr(b, "<g id='r");
        SB.writeUint(b, segmentData.ringId);
        SB.writeStr(b, "v0s");
        SB.writeUint(b, segmentData.segId);
        SB.writeStr(b, "'>");

        //draw the inner span
        drawSpan(
            b,
            segmentData.innerSpanType, 
            segmentData.start, 
            segmentData.fin, 
            segmentData.inner, 
            segmentData.padInner, 
            Variant.Inner, 
            segmentData.colour,
            rand);

        //draw the outer span
        drawSpan(
            b,
            segmentData.outerSpanType, 
            segmentData.start, 
            segmentData.fin, 
            segmentData.outer, 
            segmentData.padOuter, 
            Variant.Outer, 
            segmentData.colour,
            rand);

        //draw the edges (matching)
        if (segmentData.edge == EdgeType.Simple)
        {
            simple(
                b, 
                segmentData.start, 
                segmentData.fin, 
                segmentData.outer, 
                segmentData.padOuter, 
                segmentData.thick, 
                segmentData.colour,
                rand);
        }

        int radius = segmentData.inner + ((segmentData.outer-segmentData.inner) / 2);

        if (segmentData.fill == FillType.Block)
        {
            blck(
                b, 
                segmentData.start, 
                segmentData.fin, 
                radius, 
                segmentData.padOuter, 
                segmentData.thick, 
                segmentData.colour, 
                rand);
        }
        else if (segmentData.fill == FillType.Increment)
        {
            inc(
                b, 
                segmentData.start, 
                segmentData.fin, 
                radius, 
                segmentData.padOuter, 
                segmentData.thick, 
                segmentData.colour, 
                rand);
        }
        else if (segmentData.fill == FillType.Text)
        {
            if (!(segmentData.thick < 5 || segmentData.fin < 30))
            {
                text(
                    b, 
                    segmentData.start, 
                    segmentData.fin, 
                    segmentData.inner, 
                    segmentData.colour, 
                    rand);                    
            }
        }
        else if (segmentData.fill == FillType.Hollow)
        {
            hollow(
                b, 
                segmentData.start, 
                segmentData.fin, 
                segmentData.inner, 
                segmentData.padOuter, 
                segmentData.thick, 
                segmentData.colour,
                rand);
        }
        else if (segmentData.fill == FillType.Comp)
        {
            blck(
                b, 
                segmentData.start, 
                segmentData.fin, 
                radius, 
                segmentData.padOuter, 
                segmentData.thick, 
                segmentData.colour, 
                rand);

            inc(
                b, 
                segmentData.start, 
                segmentData.fin, 
                radius, 
                segmentData.padOuter, 
                segmentData.thick, 
                segmentData.colour, 
                rand);

            hollow(
                b, 
                segmentData.start, 
                segmentData.fin, 
                segmentData.inner, 
                segmentData.padOuter, 
                segmentData.thick, 
                segmentData.colour,
                rand);

            if (!(segmentData.thick < 5 || segmentData.fin < 30))
            {
                text(
                    b,
                    segmentData.start, 
                    segmentData.fin, 
                    segmentData.inner, 
                    segmentData.colour, 
                    rand);
            }
        }

        SB.writeStr(b, "</g>");
    }
    
    /*fill types*/

    function hollow(
        StringBuilder memory b,
        int start, 
        int fin, 
        int radius, 
        PadType pad, 
        int thickness, 
        uint col,
        Rand memory rand) 
        view private
    {
        int padding     = pad == PadType.Single ? int(2) : int(0);
        int angleStart = start+padding;
        int angleEnd   = fin-(padding*2);
        int innerRad   = radius + padding;
        int outerRad   = radius + (thickness-padding);
        int centreRad  = 320 + innerRad;
        int len         = centreRad + thickness - (padding*2);

        SB.writeStr(b, "<g transform='rotate(");
        SB.writeInt(b, angleStart);
        SB.writeStr(b, " 320 320)'><line y1='320' x1='");
        SB.writeInt(b, centreRad);
        SB.writeStr(b, "' y2='320' x2='");
        SB.writeInt(b, len);
        SB.writeStr(b, "'  stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "'/><circle r='");
        SB.writeInt(b, innerRad);
        SB.writeStr(b, "' cx='320' cy='320' fill='none' pathLength='360' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "' stroke-dasharray='");
        SB.writeInt(b, angleEnd);
        SB.writeStr(b, " 360'/><circle r='");
        SB.writeInt(b, outerRad);
        SB.writeStr(b, "' cx='320' cy='320' fill='none' pathLength='360' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "' stroke-dasharray='");
        SB.writeInt(b, angleEnd);
        SB.writeStr(b, " 360'/><g transform='rotate(");
        SB.writeInt(b, angleEnd);
        SB.writeStr(b, " 320 320)'><line y1='320' x1='");
        SB.writeInt(b, centreRad);
        SB.writeStr(b, "' y2='320' x2='");
        SB.writeInt(b, len);
        SB.writeStr(b, "'  stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "'/></g></g>");
    }

    function randomStrokeWidth(Rand memory rand) 
        private pure returns(string memory)
    {
        return Random.range(rand, 0, 9) > 6 ? "0.6" : "0.3";
    }

    function text(
        StringBuilder memory b,
        int start, 
        int fin, 
        int radius, 
        uint col, 
        Rand memory rand) 
        view private
    {
        int padding     = 2;
        int angleStart = start - padding;
        uint textId = Random.value(rand);
        
        string[12] memory sym = ["0.421", "0.36", "0.73","0.421", "0.36", "0.73","0.421", "0.36", "0.73", "+", "^", "_"];

        string memory chars = sym[uint(Random.range(rand, 0, int(sym.length)-1))];

        radius += FONT_SIZE + padding;

        SB.writeStr(b, "<g transform='rotate(");
        SB.writeInt(b, angleStart-180+fin);
        SB.writeStr(b, " 320 320)'><path id='text_path_");
        SB.writeUint(b, textId);
        SB.writeStr(b, "' d='M");
        SB.writeInt(b, 320-radius);
        SB.writeStr(b, ", 320 a1, 1 0 0, 0 ");
        SB.writeInt(b, radius*2);
        SB.writeStr(b, ", 0' pathLength='100' fill='none' stroke-width='0' stroke='red'/><text x='0%' style='fill:#");
        SB.writeRgb(b, col);
        SB.writeStr(b, ";'><textPath href='#text_path_");
        SB.writeUint(b, textId);
        SB.writeStr(b, "' pointer-events='none'>");
        SB.writeStr(b, chars);
        SB.writeStr(b, "</textPath></text></g>");
    }

    function inc(
        StringBuilder memory b,
        int start, 
        int fin, 
        int radius, 
        PadType pad, 
        int thickness, 
        uint col, 
        Rand memory rand) 
        view private
    {
        int padding     = (pad == PadType.Single) ? int(4) : int(0);
        int angleStart = start + padding / 2;
        int angleEnd   = fin - (padding);
        int stroke      = thickness - padding;

        uint incId = Random.value(rand);

        SB.writeStr(b, "<g transform='rotate(");
        SB.writeInt(b, angleStart);
        SB.writeStr(b, " 320 320)'><clipPath id='inc_cutter_");
        SB.writeUint(b, incId);
        SB.writeStr(b, "'><rect x='0' y='0' width='640' height='320' stroke='black' fill='none' transform='rotate(");
        SB.writeInt(b, angleEnd);
        SB.writeStr(b, ", 320, 320)' /></clipPath><path d='M");
        SB.writeInt(b, 320-radius);
        SB.writeStr(b, ", 320 a1, 1 0 0, 0 ");
        SB.writeInt(b, radius*2);
        SB.writeStr(b, ", 0' pathLength='100' fill='none' stroke-width='");
        SB.writeInt(b, stroke);
        SB.writeStr(b, "' stroke-opacity='0.4' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-dasharray='0.05 1' clip-path='url(#inc_cutter_");
        SB.writeUint(b, incId);
        SB.writeStr(b, ")'/></g>");
    }

    function blck(
        StringBuilder memory b,
        int start, 
        int /*fin*/, 
        int radius, 
        PadType pad, 
        int thickness, 
        uint col, 
        Rand memory rand) 
        view private
    {
        int padding     = (pad == PadType.Single) ? int(4) : int(0);
        int angleStart = start + padding / 2;
        int stroke      = thickness - padding;
        int opac        = Random.range(rand, 2, 8);


        SB.writeStr(b, "<g transform='rotate(");
        SB.writeInt(b, angleStart);
        SB.writeStr(b, " 320 320)'><circle r='");
        SB.writeInt(b, radius);
        SB.writeStr(b, "' cx='320' cy='320' fill='none' pathLength='359' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-opacity='");
        SB.writeInt(b, opac);
        SB.writeStr(b, "%' stroke-width='");
        SB.writeInt(b, stroke);
        SB.writeStr(b, "' stroke-dasharray='");
        SB.writeInt(b, angleStart);
        SB.writeStr(b, " ");
        SB.writeInt(b, 360 - angleStart);
        SB.writeStr(b, "'/></g>");
    }

    /*edge types*/

    function simple(
        StringBuilder memory b,
        int start, 
        int fin, 
        int radius, 
        PadType pad, 
        int len, 
        uint col,
        Rand memory rand) 
        view private
    {
        int padding = (pad == PadType.Single) ? int(1) : int(0);
        int angleStart = start + padding;
        int angleEnd = fin - (padding * 2);
        int centreRad = 320 + radius;
        int edgeLength = centreRad - len;

        SB.writeStr(b, "<g transform='rotate(");
        SB.writeInt(b, angleStart);
        SB.writeStr(b, " 320 320)'><line y1='320' x1='");
        SB.writeInt(b, centreRad);
        SB.writeStr(b, "' y2='320' x2='");
        SB.writeInt(b, edgeLength);
        SB.writeStr(b, "'  stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "'/><g transform='rotate(");
        SB.writeInt(b, angleEnd);
        SB.writeStr(b, " 320 320)'><line y1='320' x1='");
        SB.writeInt(b, centreRad);
        SB.writeStr(b, "' y2='320' x2='");
        SB.writeInt(b, edgeLength);
        SB.writeStr(b, "'  stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "'/></g></g>");
    }

    /*spans*/

    function brk(
        StringBuilder memory b,
        int start, 
        int fin, 
        int radius, 
        PadType pad, 
        Variant variant, 
        uint col, 
        Rand memory rand) 
        view private
    {
        int padding     = (pad == PadType.Single) ? int(1) : int(0);
        int angleStart = start + padding;
        int angleEnd   = fin - (padding * 2);
        int centreRad  = 320 + radius;
        int brkSize    = 2;
        int brkOffset = (variant == Variant.Inner) ? centreRad + brkSize : centreRad - brkSize;

        //uint brkId = Rand.next(rand);

        SB.writeStr(b, "<g><circle r='");
        SB.writeInt(b, radius);
        SB.writeStr(b, "' cx='320' cy='320' fill='none' pathLength='359' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "' stroke-dasharray='");
        SB.writeInt(b, angleStart);
        SB.writeStr(b, " ");
        SB.writeInt(b, 360 - angleStart);
        SB.writeStr(b, "'/><line y1='320' x1='");
        SB.writeInt(b, centreRad);
        SB.writeStr(b, "' y2='320' x2='");
        SB.writeInt(b, brkOffset);
        SB.writeStr(b, "' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "'/><g transform='rotate(");
        SB.writeInt(b, angleEnd);
        SB.writeStr(b, " 320 320)'><line y1='320' x1='");
        SB.writeInt(b, centreRad);
        SB.writeStr(b, "' y2='320' x2='");
        SB.writeInt(b, brkOffset);
        SB.writeStr(b, "' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "'/></g></g>");
    }

   function ang(
       StringBuilder memory b,
       int start, 
       int fin, 
       int radius, 
       PadType pad, 
       Variant variant, 
       uint col,
       Rand memory rand) 
       view private
   {
        int padding = (pad == PadType.Single) ? int(1) : int(0);
        int angleStart = start + padding;
        int angleEnd = fin - (padding * 2);
        int angsSize = 2;
        int centreRad = 320 + radius;
        int centreAng = (variant == Variant.Inner) ? centreRad + angsSize : centreRad - angsSize;
        int opac = Random.range(rand, 10, 100);
        
        SB.writeStr(b, "<g transform='rotate(");
        SB.writeInt(b, angleStart);
        SB.writeStr(b, " 320 320)'><circle r='");
        SB.writeInt(b, radius);
        SB.writeStr(b, "' cx='320' cy='320' fill='none' pathLength='360' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "'  stroke-opacity='");
        SB.writeInt(b, opac);
        SB.writeStr(b, "%' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "' stroke-dasharray= '");
        SB.writeInt(b, angleEnd);
        SB.writeStr(b, " 360'/><polyline points='");
        SB.writeInt(b, centreAng);
        SB.writeStr(b, ", 320  ");
        SB.writeInt(b, centreRad);
        SB.writeStr(b, ", 320 ");
        SB.writeInt(b, centreRad);
        SB.writeStr(b, ", ");
        SB.writeInt(b, 320+angsSize);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' fill='none'/><g transform='rotate(");
        SB.writeInt(b, angleEnd);
        SB.writeStr(b, " 320 320)'><polyline points='");
        SB.writeInt(b, centreAng);
        SB.writeStr(b, ", 320 ");
        SB.writeInt(b, centreRad);
        SB.writeStr(b, ", 320 ");
        SB.writeInt(b, centreRad);
        SB.writeStr(b, ", ");
        SB.writeInt(b, 320-angsSize);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' fill='none'/></g></g>");
   }

   function cap(
       StringBuilder memory b,
       int start, 
       int fin, 
       int radius, 
       PadType pad, 
       uint col,
       Rand memory rand) 
       view private
   {
        int padding = (pad == PadType.Single) ? int(1) : int(0);
        int angleStart = start + padding;
        int angleEnd = fin - (padding * 2);
        int gap = angleEnd - 2;
        

        SB.writeStr(b, "<g transform='rotate(");
        SB.writeInt(b, angleStart);
        SB.writeStr(b, " 320 320)'><circle r='");
        SB.writeInt(b, radius);
        SB.writeStr(b, "' cx='320' cy='320' fill='none' pathLength='360' stroke-opacity='20%' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "' stroke-dasharray='");
        SB.writeInt(b, angleEnd);
        SB.writeStr(b, " 360'/><circle r='");
        SB.writeInt(b, radius);
        SB.writeStr(b, "' cx='320' cy='320' fill='none' pathLength='360' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "' stroke-dasharray='1 ");
        SB.writeInt(b, gap);
        SB.writeStr(b, " 1 360'/></g>");
   }

   function dotted(
       StringBuilder memory b,
       int start, 
       int fin, 
       int radius, 
       PadType pad, 
       uint col, 
       Rand memory rand) 
       view private
   {
        int padding     = (pad == PadType.Single) ? int(1) : int(0);
        int angleStart = start + padding;
        int angleEnd   = fin - (padding * 2);
        int gap = angleEnd - 2;
        int opac = Random.range(rand, 10, 100);

        uint dotId = Random.value(rand);

        SB.writeStr(b, "<g transform='rotate(");
        SB.writeInt(b, angleStart);
        SB.writeStr(b, " 320 320)'><clipPath id='dot_cutter_");
        SB.writeUint(b, dotId);
        SB.writeStr(b, "'><rect x='0' y='0' width='640' height='320' stroke='black' fill='none' transform='rotate(");
        SB.writeInt(b, angleEnd);
        SB.writeStr(b, ", 320, 320)' /></clipPath><path d='M");
        SB.writeInt(b, 320-radius);
        SB.writeStr(b, ", 320 a1, 1 0 0, 0 ");
        SB.writeInt(b, radius*2);
        SB.writeStr(b, ", 0' pathLength='100' fill='none' stroke-opacity='");
        SB.writeInt(b, opac);
        SB.writeStr(b, "%' stroke-width='0.4' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-dasharray='0.25 0.25' clip-path='url(#dot_cutter_");
        SB.writeUint(b, dotId);
        SB.writeStr(b, ")'/><circle r='");
        SB.writeInt(b, radius);
        SB.writeStr(b, "' cx='320' cy='320' fill='none' pathLength='359' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='0.4' stroke-dasharray='1 ");
        SB.writeInt(b, gap);
        SB.writeStr(b, " 1 360'/></g>");
   }

   function arc(
       StringBuilder memory b, 
       int start, 
       int /*fin*/, 
       int radius, 
       PadType pad, 
       uint col, 
       Rand memory rand) 
       view private
   {
        int padding     = (pad == PadType.Single) ? int(1) : int(0);
        int angleStart = start + padding;

        SB.writeStr(b, "<g><circle r='");
        SB.writeInt(b, radius);
        SB.writeStr(b, "' cx='320' cy='320' fill='none' pathLength='359' stroke='#");
        SB.writeRgb(b, col);
        SB.writeStr(b, "' stroke-width='");
        SB.writeStr(b, randomStrokeWidth(rand));
        SB.writeStr(b, "' stroke-dasharray='");
        SB.writeInt(b, angleStart);
        SB.writeStr(b, " ");
        SB.writeInt(b, 360 - angleStart);
        SB.writeStr(b, "'/></g>");
   }
}

contract CypherMetadata is Ownable
{   
    using Strings for uint256;

    CypherDrawing _drawing;
    string private _imageBaseUri;

    constructor(
        CypherDrawing drawing,
        string memory imageBaseUri)
    {
        _drawing = drawing;
        _imageBaseUri = imageBaseUri;
    }

    function tokenURI(
        uint256 tokenId, 
        bytes32 hash,
        uint256 generation,
        bool isFirstTokenInGeneration) 
        public view returns(string memory) 
    {
        return string(abi.encodePacked("data:application/json;utf8,{"
            "\"image\":\"data:image/svg+xml;utf8,",
            _drawing.generate(hash), "\",",
            _commonMetadata(tokenId, hash, generation, isFirstTokenInGeneration),
            "}"));
    }

    function metadata(
        uint256 tokenId,
        bytes32 hash, 
        uint256 generation,
        bool isFirstTokenInGeneration)
        public view returns(string memory)
    {
        string memory imageUri = bytes(_imageBaseUri).length > 0 ? string(abi.encodePacked(_imageBaseUri, tokenId.toString())) : "";

        return string(abi.encodePacked("{"
            "\"external_url\": \"", imageUri, "\",",
            "\"image\": \"", imageUri, "\",",
            _commonMetadata(tokenId, hash, generation, isFirstTokenInGeneration),
            "}"));
    }

    function setDrawing(CypherDrawing drawing)
        public onlyOwner
    {
        _drawing = drawing;
    }

    function setImageBaseUri(string memory imageBaseUri)
        public onlyOwner
    {
        _imageBaseUri = imageBaseUri;
    }

    function _commonMetadata(
        uint256 tokenId, 
        bytes32 hash, 
        uint256 generation,
        bool isFirstTokenInGeneration)
        private view returns(string memory)
    {
        CypherAttributes memory attributes = _drawing.getAttributes(hash);
        
        int overall = 0;
        for (uint i = 0; i < 8; ++i)
        {
            overall += attributes.density[i] * attributes.intricacy[i];
        }
        overall >>= 3;

        StringBuilder memory b = SB.create(2048);
        
        SB.writeStr(b, "\"description\": \"Cypher is a generative art project by Hideki Tsukamoto comprised of 1024 tokens calculated and drawn via smart-contract, by the Ethereum Virtual Machine. Cypher is part one of the 'Apex' series.\","
            "\"name\": \"Cypher #"); 
        SB.writeUint(b, tokenId);
        SB.writeStr(b, "\","
            "\"background_color\": \"1a181b\","
            "\"attributes\": [");
        if (tokenId == 0)
        {
            SB.writeStr(b, "{"
                "\"trait_type\": \"Edition\","
                "\"value\": \"Genesis\""
                "},");
        }
        else if (tokenId == 1)
        {
            SB.writeStr(b, "{"
                "\"trait_type\": \"Edition\","
                "\"value\": \"Primary\""
                "},");
        }
        else if (tokenId == 2)
        {
            SB.writeStr(b, "{"
                "\"trait_type\": \"Edition\","
                "\"value\": \"Secondary\""
                "},");
        }
        else if (tokenId == 3)
        {
            SB.writeStr(b, "{"
                "\"trait_type\": \"Edition\","
                "\"value\": \"Tertiary\""
                "},");
        }
        if (isFirstTokenInGeneration)
        {
            SB.writeStr(b, "{"
                "\"trait_type\": \"Edition\","
                "\"value\": \"Generation Genesis\""
                "},");
        }
        SB.writeStr(b, "{"
            "\"trait_type\": \"Generation\"," 
            "\"value\": \"");
        SB.writeUint(b, generation);
        SB.writeStr(b, "\"},"
            "{"
                "\"trait_type\": \"Intricacy\","
                "\"value\":\"");
        SB.writeStr(b, _attributeValueString_0_32(overall));
        SB.writeStr(b, "\"},"
            "{"
                "\"trait_type\": \"Intricacy Value\","
                "\"max_value\": 32,"
                "\"value\":");
        SB.writeInt(b, overall);
        SB.writeStr(b, "},"
            "{"
                "\"trait_type\": \"Chaos\","
                "\"value\":\"");
        SB.writeStr(b, _attributeValueString_0_32(attributes.chaos));
        SB.writeStr(b, "\"},"
            "{"
                "\"trait_type\": \"Chaos Value\","
                "\"max_value\": 32,"
                "\"value\":");
        SB.writeInt(b, attributes.chaos);
        SB.writeStr(b, "},"
            "{"
                "\"trait_type\": \"Channels\","
                "\"value\":\"");
        SB.writeStr(b, _attributeValueString_0_8(attributes.numChannels));
        SB.writeStr(b, "\"},"
            "{"
                "\"trait_type\": \"Channels Value\","
                "\"max_value\": 8,"
                "\"value\":");
        SB.writeInt(b, attributes.numChannels);
        SB.writeStr(b, "},"
            "{"
                "\"trait_type\": \"Decay\","
                "\"value\":\"");
        SB.writeStr(b, _attributeValueString_0_32(attributes.decay));
        SB.writeStr(b, "\"},"
            "{"
                "\"trait_type\": \"Decay Value\","
                "\"max_value\": 32,"
                "\"value\":");
        SB.writeInt(b, attributes.decay);
        SB.writeStr(b, "},"
            "{"
                "\"trait_type\": \"Level\","
                "\"value\":\"");
        SB.writeUint(b, attributes.colorset + 1);
        SB.writeStr(b, "\"},"
            "{"
                "\"trait_type\": \"Level Value\","
                "\"max_value\": 5,"
                "\"value\":");
        SB.writeUint(b, attributes.colorset + 1);
        SB.writeStr(b, "}"
            "]");

        return SB.getString(b);
    }

    function _attributeValueString_0_32(int256 value)
        private pure returns(string memory)
    {
        if (value == 0)
        {
            return "Void";
        }
        else if (value <= 2)
        {
            return "Marginal";
        }
        else if (value <= 8)
        {
            return "Low";
        }
        else if (value <= 23)
        {
            return "Average";
        }
        else if (value <= 29)
        {
            return "High";
        }
        else if (value <= 31)
        {
            return "Super";
        }
        else
        {
            return "Extreme";
        }
    }

    function _attributeValueString_0_8(int256 value) 
        private pure returns(string memory)
    {
        if (value == 0)
        {
            return "Void";
        }
        else if (value <= 1)
        {
            return "Marginal";
        }
        else if (value <= 2)
        {
            return "Low";
        }
        else if (value <= 5)
        {
            return "Average";
        }
        else if (value <= 6)
        {
            return "High";
        }
        else if (value <= 7)
        {
            return "Super";
        }
        else
        {
            return "Extreme";
        }
    }
}



contract Cypher is ERC721Enumerable, Ownable
{
    uint256 private _maxTokenInvocations;
    uint256 private _reservedInvocations;
    uint256 private _auctionStartBlock;
    uint256 private _auctionEndBlock;
    uint256 private _initialFee;
    uint256 private _initialDuration;
    uint256 private _maxHalvings;
    address payable private _recipient;
    CypherDrawing private _drawing;
    CypherMetadata private _metadata;
    mapping(uint256 => bytes32) private _hashes;
    mapping(uint256 => uint256) private _generation;
    bool _isLocked;

    struct ConstructorArgs
    {
        string name;
        string symbol;
        uint256 maxTokenInvocations;
        uint256 reservedInvocations;
        uint256 auctionStartBlock;
        uint256 initialFee;
        uint256 initialDuration;
        uint256 maxHalvings;
        address payable recipient;
        CypherDrawing drawing;
        CypherMetadata meta;
    }

    constructor(
        ConstructorArgs memory args)
        ERC721(args.name, args.symbol)
    {
        _maxTokenInvocations = args.maxTokenInvocations;
        _reservedInvocations = args.reservedInvocations;
        _auctionStartBlock = args.auctionStartBlock;
        _initialFee = args.initialFee;
        _initialDuration = args.initialDuration;
        _maxHalvings = args.maxHalvings;
        _recipient = args.recipient;
        _drawing = args.drawing;
        _metadata = args.meta;

        // minting #0
        bytes32 hash = keccak256(
            abi.encodePacked(uint(0),
                            blockhash(block.number - 1)));

        _hashes[0] = hash;
        _generation[0] = 0;
        _safeMint(msg.sender, 0);
    }

    modifier isUnlocked() 
    {
        require(!_isLocked, "Contract is locked");
        _;
    }

    function lock() public onlyOwner
    {
        _isLocked = true;
    }

    function setMaxTokenInvocations(uint256 maxTokenInvocations)
        public onlyOwner isUnlocked
    {
        _maxTokenInvocations = maxTokenInvocations;
    }

    function setReservedInvocations(uint256 reservedInvocations)
        public onlyOwner isUnlocked
    {
        _reservedInvocations = reservedInvocations;
    }

    function setAuctionStartBlock(uint256 auctionStartBlock)
        public onlyOwner isUnlocked
    {
        _auctionStartBlock = auctionStartBlock;
    }

    function setInitialFee(uint256 initialFee)
        public onlyOwner isUnlocked
    {
        _initialFee = initialFee;
    }

    function setInitialDuration(uint256 initialDuration)
        public onlyOwner isUnlocked
    {
        _initialDuration = initialDuration;
    }

    function setMaxHalvings(uint256 maxHalvings)
        public onlyOwner isUnlocked
    {
        _maxHalvings = maxHalvings;
    }

    function setRecipient(address payable recipient)
        public onlyOwner
    {
        _recipient = recipient;
    }

    function setDrawingContract(CypherDrawing drawing)
        public onlyOwner isUnlocked
    {
        _drawing = drawing;
    }

    function setMetadataContract(CypherMetadata meta)
        public onlyOwner
    {
        _metadata = meta;
    }

    function getInfo()
        public view returns(
            bool hasStarted,
            bool hasEnded,
            uint256 blocksUntilAuctionStart,
            uint256 currentGeneration,
            uint256 currentFee,
            uint256 blocksUntilNextHalving,
            uint256 currentInvocationCount,
            uint256 maxTokenInvocations)
    {
        uint256 nextBlock = block.number + 1;
        if (nextBlock >= _auctionStartBlock)
        {
            hasStarted = _auctionStartBlock != 0;

            blocksUntilAuctionStart = 0;
        }
        else
        {
            hasStarted = false;

            blocksUntilAuctionStart = _auctionStartBlock - nextBlock;
        }

        if (_auctionEndBlock == 0)
        {
            hasEnded = false;

            (currentGeneration, 
            currentFee, 
            blocksUntilNextHalving) = _getAuctionState(nextBlock);
        }
        else
        {
            hasEnded = true;

            (currentGeneration, 
            currentFee, ) = _getAuctionState(_auctionEndBlock);
            blocksUntilNextHalving = 0;
        }

        currentInvocationCount = totalSupply();
        maxTokenInvocations = _maxTokenInvocations;
    }

    function purchase() 
        public payable returns (uint256 tokenId)
    {
        require(_auctionStartBlock > 0 && block.number >= _auctionStartBlock, 
                "auction hasn't started yet");

        uint256 totalInvocations = totalSupply();
        uint256 invocationsRemaining = _maxTokenInvocations - totalInvocations;
        
        require(invocationsRemaining > _reservedInvocations ||
            (invocationsRemaining > 0 && msg.sender == owner()), 
            "max token invocations reached");

        uint256 blockNumber = block.number;
        if (_auctionEndBlock != 0)
        {
            blockNumber = _auctionEndBlock;
        }
        else if (invocationsRemaining == (_reservedInvocations + 1))
        {
            _auctionEndBlock = block.number;
        }

        uint256 generation;
        uint256 fee;
        (generation, fee, ) = _getAuctionState(blockNumber);

        require(msg.value == fee, "value doesn't equal cost");
        
        (bool success, ) = _recipient.call{value:msg.value}("");
        require(success, "Transfer failed.");
        
        tokenId = totalInvocations;
        bytes32 hash = keccak256(
            abi.encodePacked(tokenId, 
                            blockhash(block.number - 1),
                            tokenId > 0 ? _hashes[tokenId - 1] : bytes32(0)));
        
        _hashes[tokenId] = hash;
        _generation[tokenId] = generation;
        _safeMint(msg.sender, tokenId);
        
        return tokenId;
    }
 
    function metadata(uint256 tokenId)
        public view returns (string memory)
    {
        require(_exists(tokenId), "token not minted");

        return _metadata.metadata( 
            tokenId, 
            _hashes[tokenId],
            _generation[tokenId],
            _isFirstTokenInGeneration(tokenId));
    }

    function generate(uint256 tokenId) 
        public view returns (string memory)
    { 
        require(_exists(tokenId), "token not minted");
        return _drawing.generate(_hashes[tokenId]);
    }

    function tokenURI(uint256 tokenId) public view virtual override 
        returns (string memory) 
    {
        require(_exists(tokenId), "token not minted");

        return _metadata.tokenURI(
            tokenId, 
            _hashes[tokenId],
            _generation[tokenId],
            _isFirstTokenInGeneration(tokenId));
    }

    function _getAuctionState(uint256 blockNumber) private view returns (
        uint256 currentGeneration, 
        uint256 currentFee, 
        uint256 blocksUntilNextHalving)
    {
        if (_auctionStartBlock == 0)
        {
            return (0, _initialFee, 0);
        }
        
        if (blockNumber < _auctionStartBlock)
        {
            uint256 firstHalving = _auctionStartBlock + _initialDuration;
            return (0, _initialFee, firstHalving - blockNumber);
        }

        uint256 generation = 0;
        uint256 fee = _initialFee;
        uint256 duration = _initialDuration;
        uint256 nextHalving = _auctionStartBlock + duration;

        for (uint256 i = 0; i < _maxHalvings; ++i)
        {
            if (blockNumber < nextHalving)
            {
                return (generation, fee, nextHalving - blockNumber);
            }

            ++generation;
            fee >>= 1;
            duration <<= 1;
            nextHalving += duration;
        }

        return (generation, fee, 0);
    }

    function _isFirstTokenInGeneration(uint256 tokenId)
        private view returns(bool)
    {
        if (tokenId > 0)
        {
            return _generation[tokenId] != _generation[tokenId - 1];
        }

        return true;
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"maxTokenInvocations","type":"uint256"},{"internalType":"uint256","name":"reservedInvocations","type":"uint256"},{"internalType":"uint256","name":"auctionStartBlock","type":"uint256"},{"internalType":"uint256","name":"initialFee","type":"uint256"},{"internalType":"uint256","name":"initialDuration","type":"uint256"},{"internalType":"uint256","name":"maxHalvings","type":"uint256"},{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"contract CypherDrawing","name":"drawing","type":"address"},{"internalType":"contract CypherMetadata","name":"meta","type":"address"}],"internalType":"struct Cypher.ConstructorArgs","name":"args","type":"tuple"}],"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":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","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"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","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"}],"name":"generate","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInfo","outputs":[{"internalType":"bool","name":"hasStarted","type":"bool"},{"internalType":"bool","name":"hasEnded","type":"bool"},{"internalType":"uint256","name":"blocksUntilAuctionStart","type":"uint256"},{"internalType":"uint256","name":"currentGeneration","type":"uint256"},{"internalType":"uint256","name":"currentFee","type":"uint256"},{"internalType":"uint256","name":"blocksUntilNextHalving","type":"uint256"},{"internalType":"uint256","name":"currentInvocationCount","type":"uint256"},{"internalType":"uint256","name":"maxTokenInvocations","type":"uint256"}],"stateMutability":"view","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":[],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"metadata","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","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":[],"name":"purchase","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionStartBlock","type":"uint256"}],"name":"setAuctionStartBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract CypherDrawing","name":"drawing","type":"address"}],"name":"setDrawingContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"initialDuration","type":"uint256"}],"name":"setInitialDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"initialFee","type":"uint256"}],"name":"setInitialFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxHalvings","type":"uint256"}],"name":"setMaxHalvings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxTokenInvocations","type":"uint256"}],"name":"setMaxTokenInvocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract CypherMetadata","name":"meta","type":"address"}],"name":"setMetadataContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"recipient","type":"address"}],"name":"setRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reservedInvocations","type":"uint256"}],"name":"setReservedInvocations","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","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":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b5060405162003190380380620031908339810160408190526200003491620009a3565b80516020808301518251909162000051916000918501906200081c565b508051620000679060019060208401906200081c565b505050620000846200007e620001af60201b60201c565b620001b3565b6040810151600b556060810151600c556080810151600d5560a0810151600f5560c081015160105560e0810151601155610100810151601280546001600160a01b039283166001600160a01b03199182161790915561012083015160138054918416918316919091179055610140830151601480549190931691161790556000806200011260014362000bae565b406040516020016200012e929190918252602082015260400190565b60408051601f19818403018152919052805160209182012060008080527fa31547ce6245cdb9ecea19cf8c7eb9f5974025bb4075011409251ae855b30aed82905560169092527f0263c2b778d062355049effc2dece97bc6547ff8a88a3258daa512061c2153dd8290559150620001a790339062000205565b505062000c90565b3390565b600a80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b620002278282604051806020016040528060008152506200022b60201b60201c565b5050565b62000237838362000273565b620002466000848484620003c9565b6200026e5760405162461bcd60e51b8152600401620002659062000b15565b60405180910390fd5b505050565b6001600160a01b038216620002cb5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015260640162000265565b6000818152600260205260409020546001600160a01b031615620003325760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640162000265565b6200034060008383620004f9565b6001600160a01b03821660009081526003602052604081208054600192906200036b90849062000b93565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6000620003ea846001600160a01b0316620005d560201b6200151c1760201c565b15620004ed57604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906200042490339089908890889060040162000abf565b602060405180830381600087803b1580156200043f57600080fd5b505af192505050801562000472575060408051601f3d908101601f191682019092526200046f9181019062000970565b60015b620004d2573d808015620004a3576040519150601f19603f3d011682016040523d82523d6000602084013e620004a8565b606091505b508051620004ca5760405162461bcd60e51b8152600401620002659062000b15565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050620004f1565b5060015b949350505050565b620005118383836200026e60201b620008c91760201c565b6001600160a01b0383166200056f576200056981600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b62000595565b816001600160a01b0316836001600160a01b0316146200059557620005958382620005db565b6001600160a01b038216620005af576200026e8162000688565b826001600160a01b0316826001600160a01b0316146200026e576200026e828262000742565b3b151590565b60006001620005f5846200079360201b62000f711760201c565b62000601919062000bae565b60008381526007602052604090205490915080821462000655576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b6008546000906200069c9060019062000bae565b60008381526009602052604081205460088054939450909284908110620006c757620006c762000c64565b906000526020600020015490508060088381548110620006eb57620006eb62000c64565b600091825260208083209091019290925582815260099091526040808220849055858252812055600880548062000726576200072662000c4e565b6001900381819060005260206000200160009055905550505050565b60006200075a836200079360201b62000f711760201c565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b60006001600160a01b038216620008005760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b606482015260840162000265565b506001600160a01b031660009081526003602052604090205490565b8280546200082a9062000bfb565b90600052602060002090601f0160209004810192826200084e576000855562000899565b82601f106200086957805160ff191683800117855562000899565b8280016001018555821562000899579182015b82811115620008995782518255916020019190600101906200087c565b50620008a7929150620008ab565b5090565b5b80821115620008a75760008155600101620008ac565b80516001600160a01b0381168114620008da57600080fd5b919050565b600082601f830112620008f157600080fd5b81516001600160401b03808211156200090e576200090e62000c7a565b604051601f8301601f19908116603f0116810190828211818310171562000939576200093962000c7a565b816040528381528660208588010111156200095357600080fd5b6200096684602083016020890162000bc8565b9695505050505050565b6000602082840312156200098357600080fd5b81516001600160e01b0319811681146200099c57600080fd5b9392505050565b600060208284031215620009b657600080fd5b81516001600160401b0380821115620009ce57600080fd5b908301906101608286031215620009e457600080fd5b620009ee62000b67565b825182811115620009fe57600080fd5b62000a0c87828601620008df565b82525060208301518281111562000a2257600080fd5b62000a3087828601620008df565b60208301525060408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e0820152610100915062000a84828401620008c2565b82820152610120915062000a9a828401620008c2565b82820152610140915062000ab0828401620008c2565b91810191909152949350505050565b600060018060a01b03808716835280861660208401525083604083015260806060830152825180608084015262000afe8160a085016020870162000bc8565b601f01601f19169190910160a00195945050505050565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60405161016081016001600160401b038111828210171562000b8d5762000b8d62000c7a565b60405290565b6000821982111562000ba95762000ba962000c38565b500190565b60008282101562000bc35762000bc362000c38565b500390565b60005b8381101562000be557818101518382015260200162000bcb565b8381111562000bf5576000848401525b50505050565b600181811c9082168062000c1057607f821691505b6020821081141562000c3257634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6124f08062000ca06000396000f3fe60806040526004361061017e5760003560e01c806301ffc9a714610183578063066208de146101b857806306fdde03146101da578063081812fc146101fc578063092e59c614610234578063095ea7b31461025457806318160ddd1461027457806323b872dd146102935780632464bb5a146102b35780632f745c59146102d35780633bbed4a0146102f357806342842e0e146103135780634a7dd523146103335780634f6ccce7146103535780635a9b0b89146103735780636352211e146103c557806364edfbf0146103e557806370a08231146103ed578063715018a61461040d578063814121a5146104225780638da5cb5b1461044257806395d89b4114610457578063a22cb4651461046c578063b88d4fde1461048c578063bdf1e06d146104ac578063c13fabaf146104cc578063c87b56dd146104ec578063e3684e391461050c578063e5187f431461052c578063e985e9c51461054c578063ec7c64491461056c578063f2fde38b1461058c578063f83d08ba146105ac575b600080fd5b34801561018f57600080fd5b506101a361019e36600461209c565b6105c1565b60405190151581526020015b60405180910390f35b3480156101c457600080fd5b506101d86101d336600461214c565b6105ec565b005b3480156101e657600080fd5b506101ef61064c565b6040516101af91906121ce565b34801561020857600080fd5b5061021c61021736600461214c565b6106de565b6040516001600160a01b0390911681526020016101af565b34801561024057600080fd5b506101d861024f36600461214c565b610766565b34801561026057600080fd5b506101d861026f366004612070565b6107bd565b34801561028057600080fd5b506008545b6040519081526020016101af565b34801561029f57600080fd5b506101d86102ae366004611f4e565b6108ce565b3480156102bf57600080fd5b506101d86102ce36600461214c565b6108ff565b3480156102df57600080fd5b506102856102ee366004612070565b610956565b3480156102ff57600080fd5b506101d861030e366004611ef1565b6109ec565b34801561031f57600080fd5b506101d861032e366004611f4e565b610a3d565b34801561033f57600080fd5b506101ef61034e36600461214c565b610a58565b34801561035f57600080fd5b5061028561036e36600461214c565b610b15565b34801561037f57600080fd5b50610388610ba8565b6040805198151589529615156020890152958701949094526060860192909252608085015260a084015260c083015260e0820152610100016101af565b3480156103d157600080fd5b5061021c6103e036600461214c565b610c3c565b610285610cb3565b3480156103f957600080fd5b50610285610408366004611ef1565b610f71565b34801561041957600080fd5b506101d8610ff8565b34801561042e57600080fd5b506101d861043d36600461214c565b611033565b34801561044e57600080fd5b5061021c61108a565b34801561046357600080fd5b506101ef611099565b34801561047857600080fd5b506101d861048736600461203d565b6110a8565b34801561049857600080fd5b506101d86104a7366004611f8f565b611169565b3480156104b857600080fd5b506101d86104c7366004611ef1565b6111a1565b3480156104d857600080fd5b506101d86104e736600461214c565b611215565b3480156104f857600080fd5b506101ef61050736600461214c565b61126c565b34801561051857600080fd5b506101ef61052736600461214c565b611306565b34801561053857600080fd5b506101d8610547366004611ef1565b611368565b34801561055857600080fd5b506101a3610567366004611f15565b6113b9565b34801561057857600080fd5b506101d861058736600461214c565b6113e7565b34801561059857600080fd5b506101d86105a7366004611ef1565b61143e565b3480156105b857600080fd5b506101d86114de565b60006001600160e01b0319821663780e9d6360e01b14806105e657506105e682611522565b92915050565b336105f561108a565b6001600160a01b0316146106245760405162461bcd60e51b815260040161061b90612233565b60405180910390fd5b60175460ff16156106475760405162461bcd60e51b815260040161061b906122e3565b601055565b60606000805461065b906123c1565b80601f0160208091040260200160405190810160405280929190818152602001828054610687906123c1565b80156106d45780601f106106a9576101008083540402835291602001916106d4565b820191906000526020600020905b8154815290600101906020018083116106b757829003601f168201915b5050505050905090565b60006106e982611572565b61074a5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b606482015260840161061b565b506000908152600460205260409020546001600160a01b031690565b3361076f61108a565b6001600160a01b0316146107955760405162461bcd60e51b815260040161061b90612233565b60175460ff16156107b85760405162461bcd60e51b815260040161061b906122e3565b600f55565b60006107c882610c3c565b9050806001600160a01b0316836001600160a01b031614156108365760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b606482015260840161061b565b336001600160a01b0382161480610852575061085281336113b9565b6108bf5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f776044820152771b995c881b9bdc88185c1c1c9bdd995908199bdc88185b1b60421b606482015260840161061b565b6108c9838361158f565b505050565b6108d833826115fd565b6108f45760405162461bcd60e51b815260040161061b90612268565b6108c98383836116c7565b3361090861108a565b6001600160a01b03161461092e5760405162461bcd60e51b815260040161061b90612233565b60175460ff16156109515760405162461bcd60e51b815260040161061b906122e3565b600d55565b600061096183610f71565b82106109c35760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b606482015260840161061b565b506001600160a01b03919091166000908152600660209081526040808320938352929052205490565b336109f561108a565b6001600160a01b031614610a1b5760405162461bcd60e51b815260040161061b90612233565b601280546001600160a01b0319166001600160a01b0392909216919091179055565b6108c983838360405180602001604052806000815250611169565b6060610a6382611572565b610a7f5760405162461bcd60e51b815260040161061b906122b9565b601354600083815260156020526040908190205490516304a9bd7d60e21b81526001600160a01b03909216916312a6f5f491610ac19160040190815260200190565b60006040518083038186803b158015610ad957600080fd5b505afa158015610aed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105e691908101906120d6565b6000610b2060085490565b8210610b835760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b606482015260840161061b565b60088281548110610b9657610b96612443565b90600052602060002001549050919050565b60008080808080808080610bbd436001612366565b9050600d548110610bd857600d541515985060009650610bed565b6000985080600d54610bea919061237e565b96505b600e54610c0d5760009750610c0181611860565b91975095509350610c27565b60019750610c1c600e54611860565b509096509450600093505b6008549250600b549150509091929394959697565b6000818152600260205260408120546001600160a01b0316806105e65760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b606482015260840161061b565b600080600d54118015610cc85750600d544310155b610d115760405162461bcd60e51b815260206004820152601a602482015279185d58dd1a5bdb881a185cdb89dd081cdd185c9d1959081e595d60321b604482015260640161061b565b6000610d1c60085490565b9050600081600b54610d2e919061237e565b9050600c54811180610d635750600081118015610d635750610d4e61108a565b6001600160a01b0316336001600160a01b0316145b610daf5760405162461bcd60e51b815260206004820152601d60248201527f6d617820746f6b656e20696e766f636174696f6e732072656163686564000000604482015260640161061b565b600e54439015610dc25750600e54610ddc565b600c54610dd0906001612366565b821415610ddc5743600e555b600080610de883611860565b509092509050348114610e385760405162461bcd60e51b81526020600482015260186024820152771d985b1d5948191bd95cdb89dd08195c5d585b0818dbdcdd60421b604482015260640161061b565b6012546040516000916001600160a01b03169034908381818185875af1925050503d8060008114610e85576040519150601f19603f3d011682016040523d82523d6000602084013e610e8a565b606091505b5050905080610ece5760405162461bcd60e51b815260206004820152601060248201526f2a3930b739b332b9103330b4b632b21760811b604482015260640161061b565b859650600087600143610ee1919061237e565b4060008a11610ef1576000610f10565b60156000610f0060018d61237e565b8152602001908152602001600020545b604080516020810194909452830191909152606082015260800160408051601f19818403018152918152815160209283012060008b815260158452828120829055601690935291208590559050610f67338961194f565b5050505050505090565b60006001600160a01b038216610fdc5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b606482015260840161061b565b506001600160a01b031660009081526003602052604090205490565b3361100161108a565b6001600160a01b0316146110275760405162461bcd60e51b815260040161061b90612233565b611031600061196d565b565b3361103c61108a565b6001600160a01b0316146110625760405162461bcd60e51b815260040161061b90612233565b60175460ff16156110855760405162461bcd60e51b815260040161061b906122e3565b600c55565b600a546001600160a01b031690565b60606001805461065b906123c1565b6001600160a01b0382163314156110fd5760405162461bcd60e51b815260206004820152601960248201527822a9219b99189d1030b8383937bb32903a379031b0b63632b960391b604482015260640161061b565b3360008181526005602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b61117333836115fd565b61118f5760405162461bcd60e51b815260040161061b90612268565b61119b848484846119bf565b50505050565b336111aa61108a565b6001600160a01b0316146111d05760405162461bcd60e51b815260040161061b90612233565b60175460ff16156111f35760405162461bcd60e51b815260040161061b906122e3565b601380546001600160a01b0319166001600160a01b0392909216919091179055565b3361121e61108a565b6001600160a01b0316146112445760405162461bcd60e51b815260040161061b90612233565b60175460ff16156112675760405162461bcd60e51b815260040161061b906122e3565b601155565b606061127782611572565b6112935760405162461bcd60e51b815260040161061b906122b9565b6014546000838152601560209081526040808320546016909252909120546001600160a01b0390921691637e99d4f29185916112ce836119f2565b6040516001600160e01b031960e087901b16815260048101949094526024840192909252604483015215156064820152608401610ac1565b606061131182611572565b61132d5760405162461bcd60e51b815260040161061b906122b9565b6014546000838152601560209081526040808320546016909252909120546001600160a01b0390921691633e47894a9185916112ce836119f2565b3361137161108a565b6001600160a01b0316146113975760405162461bcd60e51b815260040161061b90612233565b601480546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b336113f061108a565b6001600160a01b0316146114165760405162461bcd60e51b815260040161061b90612233565b60175460ff16156114395760405162461bcd60e51b815260040161061b906122e3565b600b55565b3361144761108a565b6001600160a01b03161461146d5760405162461bcd60e51b815260040161061b90612233565b6001600160a01b0381166114d25760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161061b565b6114db8161196d565b50565b336114e761108a565b6001600160a01b03161461150d5760405162461bcd60e51b815260040161061b90612233565b6017805460ff19166001179055565b3b151590565b60006001600160e01b031982166380ac58cd60e01b148061155357506001600160e01b03198216635b5e139f60e01b145b806105e657506301ffc9a760e01b6001600160e01b03198316146105e6565b6000908152600260205260409020546001600160a01b0316151590565b600081815260046020526040902080546001600160a01b0319166001600160a01b03841690811790915581906115c482610c3c565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600061160882611572565b6116695760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b606482015260840161061b565b600061167483610c3c565b9050806001600160a01b0316846001600160a01b031614806116af5750836001600160a01b03166116a4846106de565b6001600160a01b0316145b806116bf57506116bf81856113b9565b949350505050565b826001600160a01b03166116da82610c3c565b6001600160a01b0316146117425760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b606482015260840161061b565b6001600160a01b0382166117a45760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b606482015260840161061b565b6117af838383611a3d565b6117ba60008261158f565b6001600160a01b03831660009081526003602052604081208054600192906117e390849061237e565b90915550506001600160a01b0382166000908152600360205260408120805460019290611811908490612366565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03868116918217909255915184939187169160008051602061249b83398151915291a4505050565b6000806000600d546000141561187f575050600f546000915081611948565b600d548410156118ba576000601054600d5461189b9190612366565b90506000600f5486836118ae919061237e565b93509350935050611948565b600f54601054600d54600092919083906118d5908390612366565b905060005b60115481101561193a57818910156119085784846118f88b8561237e565b9750975097505050505050611948565b611911856123fc565b9450600193841c939290921b916119288383612366565b9150611933816123fc565b90506118da565b509295509093506000925050505b9193909250565b611969828260405180602001604052806000815250611af5565b5050565b600a80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6119ca8484846116c7565b6119d684848484611b28565b61119b5760405162461bcd60e51b815260040161061b906121e1565b60008115611a355760166000611a0960018561237e565b815260200190815260200160002054601660008481526020019081526020016000205414159050919050565b506001919050565b6001600160a01b038316611a9857611a9381600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b611abb565b816001600160a01b0316836001600160a01b031614611abb57611abb8382611c35565b6001600160a01b038216611ad2576108c981611cd2565b826001600160a01b0316826001600160a01b0316146108c9576108c98282611d81565b611aff8383611dc5565b611b0c6000848484611b28565b6108c95760405162461bcd60e51b815260040161061b906121e1565b60006001600160a01b0384163b15611c2a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290611b6c903390899088908890600401612191565b602060405180830381600087803b158015611b8657600080fd5b505af1925050508015611bb6575060408051601f3d908101601f19168201909252611bb3918101906120b9565b60015b611c10573d808015611be4576040519150601f19603f3d011682016040523d82523d6000602084013e611be9565b606091505b508051611c085760405162461bcd60e51b815260040161061b906121e1565b805181602001fd5b6001600160e01b031916630a85bd0160e11b1490506116bf565b506001949350505050565b60006001611c4284610f71565b611c4c919061237e565b600083815260076020526040902054909150808214611c9f576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b600854600090611ce49060019061237e565b60008381526009602052604081205460088054939450909284908110611d0c57611d0c612443565b906000526020600020015490508060088381548110611d2d57611d2d612443565b6000918252602080832090910192909255828152600990915260408082208490558582528120556008805480611d6557611d6561242d565b6001900381819060005260206000200160009055905550505050565b6000611d8c83610f71565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b6001600160a01b038216611e1b5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015260640161061b565b611e2481611572565b15611e705760405162461bcd60e51b815260206004820152601c60248201527b115490cdcc8c4e881d1bdad95b88185b1c9958591e481b5a5b9d195960221b604482015260640161061b565b611e7c60008383611a3d565b6001600160a01b0382166000908152600360205260408120805460019290611ea5908490612366565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b038616908117909155905183929060008051602061249b833981519152908290a45050565b600060208284031215611f0357600080fd5b8135611f0e8161246f565b9392505050565b60008060408385031215611f2857600080fd5b8235611f338161246f565b91506020830135611f438161246f565b809150509250929050565b600080600060608486031215611f6357600080fd5b8335611f6e8161246f565b92506020840135611f7e8161246f565b929592945050506040919091013590565b60008060008060808587031215611fa557600080fd5b8435611fb08161246f565b93506020850135611fc08161246f565b92506040850135915060608501356001600160401b03811115611fe257600080fd5b8501601f81018713611ff357600080fd5b80356120066120018261233f565b61230f565b81815288602083850101111561201b57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b6000806040838503121561205057600080fd5b823561205b8161246f565b915060208301358015158114611f4357600080fd5b6000806040838503121561208357600080fd5b823561208e8161246f565b946020939093013593505050565b6000602082840312156120ae57600080fd5b8135611f0e81612484565b6000602082840312156120cb57600080fd5b8151611f0e81612484565b6000602082840312156120e857600080fd5b81516001600160401b038111156120fe57600080fd5b8201601f8101841361210f57600080fd5b805161211d6120018261233f565b81815285602083850101111561213257600080fd5b612143826020830160208601612395565b95945050505050565b60006020828403121561215e57600080fd5b5035919050565b6000815180845261217d816020860160208601612395565b601f01601f19169290920160200192915050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906121c490830184612165565b9695505050505050565b602081526000611f0e6020830184612165565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b60208082526010908201526f1d1bdad95b881b9bdd081b5a5b9d195960821b604082015260600190565b60208082526012908201527110dbdb9d1c9858dd081a5cc81b1bd8dad95960721b604082015260600190565b604051601f8201601f191681016001600160401b038111828210171561233757612337612459565b604052919050565b60006001600160401b0382111561235857612358612459565b50601f01601f191660200190565b6000821982111561237957612379612417565b500190565b60008282101561239057612390612417565b500390565b60005b838110156123b0578181015183820152602001612398565b8381111561119b5750506000910152565b600181811c908216806123d557607f821691505b602082108114156123f657634e487b7160e01b600052602260045260246000fd5b50919050565b600060001982141561241057612410612417565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146114db57600080fd5b6001600160e01b0319811681146114db57600080fdfeddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122090b21747925068e2b95a809dfd2235f6a2462418c2d9737be1b7237b1f8bdc7764736f6c634300080600330000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003782dace9d900000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000040519d946311449b33d6b03bb40892faeef12d75000000000000000000000000bd9aa94b28e6362fc610d485ce779ef6baa041aa000000000000000000000000f8ee8798f1fbd48f95ca70d2a68fdda404660b550000000000000000000000000000000000000000000000000000000000000006437970686572000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044350485200000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x60806040526004361061017e5760003560e01c806301ffc9a714610183578063066208de146101b857806306fdde03146101da578063081812fc146101fc578063092e59c614610234578063095ea7b31461025457806318160ddd1461027457806323b872dd146102935780632464bb5a146102b35780632f745c59146102d35780633bbed4a0146102f357806342842e0e146103135780634a7dd523146103335780634f6ccce7146103535780635a9b0b89146103735780636352211e146103c557806364edfbf0146103e557806370a08231146103ed578063715018a61461040d578063814121a5146104225780638da5cb5b1461044257806395d89b4114610457578063a22cb4651461046c578063b88d4fde1461048c578063bdf1e06d146104ac578063c13fabaf146104cc578063c87b56dd146104ec578063e3684e391461050c578063e5187f431461052c578063e985e9c51461054c578063ec7c64491461056c578063f2fde38b1461058c578063f83d08ba146105ac575b600080fd5b34801561018f57600080fd5b506101a361019e36600461209c565b6105c1565b60405190151581526020015b60405180910390f35b3480156101c457600080fd5b506101d86101d336600461214c565b6105ec565b005b3480156101e657600080fd5b506101ef61064c565b6040516101af91906121ce565b34801561020857600080fd5b5061021c61021736600461214c565b6106de565b6040516001600160a01b0390911681526020016101af565b34801561024057600080fd5b506101d861024f36600461214c565b610766565b34801561026057600080fd5b506101d861026f366004612070565b6107bd565b34801561028057600080fd5b506008545b6040519081526020016101af565b34801561029f57600080fd5b506101d86102ae366004611f4e565b6108ce565b3480156102bf57600080fd5b506101d86102ce36600461214c565b6108ff565b3480156102df57600080fd5b506102856102ee366004612070565b610956565b3480156102ff57600080fd5b506101d861030e366004611ef1565b6109ec565b34801561031f57600080fd5b506101d861032e366004611f4e565b610a3d565b34801561033f57600080fd5b506101ef61034e36600461214c565b610a58565b34801561035f57600080fd5b5061028561036e36600461214c565b610b15565b34801561037f57600080fd5b50610388610ba8565b6040805198151589529615156020890152958701949094526060860192909252608085015260a084015260c083015260e0820152610100016101af565b3480156103d157600080fd5b5061021c6103e036600461214c565b610c3c565b610285610cb3565b3480156103f957600080fd5b50610285610408366004611ef1565b610f71565b34801561041957600080fd5b506101d8610ff8565b34801561042e57600080fd5b506101d861043d36600461214c565b611033565b34801561044e57600080fd5b5061021c61108a565b34801561046357600080fd5b506101ef611099565b34801561047857600080fd5b506101d861048736600461203d565b6110a8565b34801561049857600080fd5b506101d86104a7366004611f8f565b611169565b3480156104b857600080fd5b506101d86104c7366004611ef1565b6111a1565b3480156104d857600080fd5b506101d86104e736600461214c565b611215565b3480156104f857600080fd5b506101ef61050736600461214c565b61126c565b34801561051857600080fd5b506101ef61052736600461214c565b611306565b34801561053857600080fd5b506101d8610547366004611ef1565b611368565b34801561055857600080fd5b506101a3610567366004611f15565b6113b9565b34801561057857600080fd5b506101d861058736600461214c565b6113e7565b34801561059857600080fd5b506101d86105a7366004611ef1565b61143e565b3480156105b857600080fd5b506101d86114de565b60006001600160e01b0319821663780e9d6360e01b14806105e657506105e682611522565b92915050565b336105f561108a565b6001600160a01b0316146106245760405162461bcd60e51b815260040161061b90612233565b60405180910390fd5b60175460ff16156106475760405162461bcd60e51b815260040161061b906122e3565b601055565b60606000805461065b906123c1565b80601f0160208091040260200160405190810160405280929190818152602001828054610687906123c1565b80156106d45780601f106106a9576101008083540402835291602001916106d4565b820191906000526020600020905b8154815290600101906020018083116106b757829003601f168201915b5050505050905090565b60006106e982611572565b61074a5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b606482015260840161061b565b506000908152600460205260409020546001600160a01b031690565b3361076f61108a565b6001600160a01b0316146107955760405162461bcd60e51b815260040161061b90612233565b60175460ff16156107b85760405162461bcd60e51b815260040161061b906122e3565b600f55565b60006107c882610c3c565b9050806001600160a01b0316836001600160a01b031614156108365760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b606482015260840161061b565b336001600160a01b0382161480610852575061085281336113b9565b6108bf5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f776044820152771b995c881b9bdc88185c1c1c9bdd995908199bdc88185b1b60421b606482015260840161061b565b6108c9838361158f565b505050565b6108d833826115fd565b6108f45760405162461bcd60e51b815260040161061b90612268565b6108c98383836116c7565b3361090861108a565b6001600160a01b03161461092e5760405162461bcd60e51b815260040161061b90612233565b60175460ff16156109515760405162461bcd60e51b815260040161061b906122e3565b600d55565b600061096183610f71565b82106109c35760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b606482015260840161061b565b506001600160a01b03919091166000908152600660209081526040808320938352929052205490565b336109f561108a565b6001600160a01b031614610a1b5760405162461bcd60e51b815260040161061b90612233565b601280546001600160a01b0319166001600160a01b0392909216919091179055565b6108c983838360405180602001604052806000815250611169565b6060610a6382611572565b610a7f5760405162461bcd60e51b815260040161061b906122b9565b601354600083815260156020526040908190205490516304a9bd7d60e21b81526001600160a01b03909216916312a6f5f491610ac19160040190815260200190565b60006040518083038186803b158015610ad957600080fd5b505afa158015610aed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105e691908101906120d6565b6000610b2060085490565b8210610b835760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b606482015260840161061b565b60088281548110610b9657610b96612443565b90600052602060002001549050919050565b60008080808080808080610bbd436001612366565b9050600d548110610bd857600d541515985060009650610bed565b6000985080600d54610bea919061237e565b96505b600e54610c0d5760009750610c0181611860565b91975095509350610c27565b60019750610c1c600e54611860565b509096509450600093505b6008549250600b549150509091929394959697565b6000818152600260205260408120546001600160a01b0316806105e65760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b606482015260840161061b565b600080600d54118015610cc85750600d544310155b610d115760405162461bcd60e51b815260206004820152601a602482015279185d58dd1a5bdb881a185cdb89dd081cdd185c9d1959081e595d60321b604482015260640161061b565b6000610d1c60085490565b9050600081600b54610d2e919061237e565b9050600c54811180610d635750600081118015610d635750610d4e61108a565b6001600160a01b0316336001600160a01b0316145b610daf5760405162461bcd60e51b815260206004820152601d60248201527f6d617820746f6b656e20696e766f636174696f6e732072656163686564000000604482015260640161061b565b600e54439015610dc25750600e54610ddc565b600c54610dd0906001612366565b821415610ddc5743600e555b600080610de883611860565b509092509050348114610e385760405162461bcd60e51b81526020600482015260186024820152771d985b1d5948191bd95cdb89dd08195c5d585b0818dbdcdd60421b604482015260640161061b565b6012546040516000916001600160a01b03169034908381818185875af1925050503d8060008114610e85576040519150601f19603f3d011682016040523d82523d6000602084013e610e8a565b606091505b5050905080610ece5760405162461bcd60e51b815260206004820152601060248201526f2a3930b739b332b9103330b4b632b21760811b604482015260640161061b565b859650600087600143610ee1919061237e565b4060008a11610ef1576000610f10565b60156000610f0060018d61237e565b8152602001908152602001600020545b604080516020810194909452830191909152606082015260800160408051601f19818403018152918152815160209283012060008b815260158452828120829055601690935291208590559050610f67338961194f565b5050505050505090565b60006001600160a01b038216610fdc5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b606482015260840161061b565b506001600160a01b031660009081526003602052604090205490565b3361100161108a565b6001600160a01b0316146110275760405162461bcd60e51b815260040161061b90612233565b611031600061196d565b565b3361103c61108a565b6001600160a01b0316146110625760405162461bcd60e51b815260040161061b90612233565b60175460ff16156110855760405162461bcd60e51b815260040161061b906122e3565b600c55565b600a546001600160a01b031690565b60606001805461065b906123c1565b6001600160a01b0382163314156110fd5760405162461bcd60e51b815260206004820152601960248201527822a9219b99189d1030b8383937bb32903a379031b0b63632b960391b604482015260640161061b565b3360008181526005602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b61117333836115fd565b61118f5760405162461bcd60e51b815260040161061b90612268565b61119b848484846119bf565b50505050565b336111aa61108a565b6001600160a01b0316146111d05760405162461bcd60e51b815260040161061b90612233565b60175460ff16156111f35760405162461bcd60e51b815260040161061b906122e3565b601380546001600160a01b0319166001600160a01b0392909216919091179055565b3361121e61108a565b6001600160a01b0316146112445760405162461bcd60e51b815260040161061b90612233565b60175460ff16156112675760405162461bcd60e51b815260040161061b906122e3565b601155565b606061127782611572565b6112935760405162461bcd60e51b815260040161061b906122b9565b6014546000838152601560209081526040808320546016909252909120546001600160a01b0390921691637e99d4f29185916112ce836119f2565b6040516001600160e01b031960e087901b16815260048101949094526024840192909252604483015215156064820152608401610ac1565b606061131182611572565b61132d5760405162461bcd60e51b815260040161061b906122b9565b6014546000838152601560209081526040808320546016909252909120546001600160a01b0390921691633e47894a9185916112ce836119f2565b3361137161108a565b6001600160a01b0316146113975760405162461bcd60e51b815260040161061b90612233565b601480546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b336113f061108a565b6001600160a01b0316146114165760405162461bcd60e51b815260040161061b90612233565b60175460ff16156114395760405162461bcd60e51b815260040161061b906122e3565b600b55565b3361144761108a565b6001600160a01b03161461146d5760405162461bcd60e51b815260040161061b90612233565b6001600160a01b0381166114d25760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161061b565b6114db8161196d565b50565b336114e761108a565b6001600160a01b03161461150d5760405162461bcd60e51b815260040161061b90612233565b6017805460ff19166001179055565b3b151590565b60006001600160e01b031982166380ac58cd60e01b148061155357506001600160e01b03198216635b5e139f60e01b145b806105e657506301ffc9a760e01b6001600160e01b03198316146105e6565b6000908152600260205260409020546001600160a01b0316151590565b600081815260046020526040902080546001600160a01b0319166001600160a01b03841690811790915581906115c482610c3c565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600061160882611572565b6116695760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b606482015260840161061b565b600061167483610c3c565b9050806001600160a01b0316846001600160a01b031614806116af5750836001600160a01b03166116a4846106de565b6001600160a01b0316145b806116bf57506116bf81856113b9565b949350505050565b826001600160a01b03166116da82610c3c565b6001600160a01b0316146117425760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b606482015260840161061b565b6001600160a01b0382166117a45760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b606482015260840161061b565b6117af838383611a3d565b6117ba60008261158f565b6001600160a01b03831660009081526003602052604081208054600192906117e390849061237e565b90915550506001600160a01b0382166000908152600360205260408120805460019290611811908490612366565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03868116918217909255915184939187169160008051602061249b83398151915291a4505050565b6000806000600d546000141561187f575050600f546000915081611948565b600d548410156118ba576000601054600d5461189b9190612366565b90506000600f5486836118ae919061237e565b93509350935050611948565b600f54601054600d54600092919083906118d5908390612366565b905060005b60115481101561193a57818910156119085784846118f88b8561237e565b9750975097505050505050611948565b611911856123fc565b9450600193841c939290921b916119288383612366565b9150611933816123fc565b90506118da565b509295509093506000925050505b9193909250565b611969828260405180602001604052806000815250611af5565b5050565b600a80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6119ca8484846116c7565b6119d684848484611b28565b61119b5760405162461bcd60e51b815260040161061b906121e1565b60008115611a355760166000611a0960018561237e565b815260200190815260200160002054601660008481526020019081526020016000205414159050919050565b506001919050565b6001600160a01b038316611a9857611a9381600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b611abb565b816001600160a01b0316836001600160a01b031614611abb57611abb8382611c35565b6001600160a01b038216611ad2576108c981611cd2565b826001600160a01b0316826001600160a01b0316146108c9576108c98282611d81565b611aff8383611dc5565b611b0c6000848484611b28565b6108c95760405162461bcd60e51b815260040161061b906121e1565b60006001600160a01b0384163b15611c2a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290611b6c903390899088908890600401612191565b602060405180830381600087803b158015611b8657600080fd5b505af1925050508015611bb6575060408051601f3d908101601f19168201909252611bb3918101906120b9565b60015b611c10573d808015611be4576040519150601f19603f3d011682016040523d82523d6000602084013e611be9565b606091505b508051611c085760405162461bcd60e51b815260040161061b906121e1565b805181602001fd5b6001600160e01b031916630a85bd0160e11b1490506116bf565b506001949350505050565b60006001611c4284610f71565b611c4c919061237e565b600083815260076020526040902054909150808214611c9f576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b600854600090611ce49060019061237e565b60008381526009602052604081205460088054939450909284908110611d0c57611d0c612443565b906000526020600020015490508060088381548110611d2d57611d2d612443565b6000918252602080832090910192909255828152600990915260408082208490558582528120556008805480611d6557611d6561242d565b6001900381819060005260206000200160009055905550505050565b6000611d8c83610f71565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b6001600160a01b038216611e1b5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015260640161061b565b611e2481611572565b15611e705760405162461bcd60e51b815260206004820152601c60248201527b115490cdcc8c4e881d1bdad95b88185b1c9958591e481b5a5b9d195960221b604482015260640161061b565b611e7c60008383611a3d565b6001600160a01b0382166000908152600360205260408120805460019290611ea5908490612366565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b038616908117909155905183929060008051602061249b833981519152908290a45050565b600060208284031215611f0357600080fd5b8135611f0e8161246f565b9392505050565b60008060408385031215611f2857600080fd5b8235611f338161246f565b91506020830135611f438161246f565b809150509250929050565b600080600060608486031215611f6357600080fd5b8335611f6e8161246f565b92506020840135611f7e8161246f565b929592945050506040919091013590565b60008060008060808587031215611fa557600080fd5b8435611fb08161246f565b93506020850135611fc08161246f565b92506040850135915060608501356001600160401b03811115611fe257600080fd5b8501601f81018713611ff357600080fd5b80356120066120018261233f565b61230f565b81815288602083850101111561201b57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b6000806040838503121561205057600080fd5b823561205b8161246f565b915060208301358015158114611f4357600080fd5b6000806040838503121561208357600080fd5b823561208e8161246f565b946020939093013593505050565b6000602082840312156120ae57600080fd5b8135611f0e81612484565b6000602082840312156120cb57600080fd5b8151611f0e81612484565b6000602082840312156120e857600080fd5b81516001600160401b038111156120fe57600080fd5b8201601f8101841361210f57600080fd5b805161211d6120018261233f565b81815285602083850101111561213257600080fd5b612143826020830160208601612395565b95945050505050565b60006020828403121561215e57600080fd5b5035919050565b6000815180845261217d816020860160208601612395565b601f01601f19169290920160200192915050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906121c490830184612165565b9695505050505050565b602081526000611f0e6020830184612165565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b60208082526010908201526f1d1bdad95b881b9bdd081b5a5b9d195960821b604082015260600190565b60208082526012908201527110dbdb9d1c9858dd081a5cc81b1bd8dad95960721b604082015260600190565b604051601f8201601f191681016001600160401b038111828210171561233757612337612459565b604052919050565b60006001600160401b0382111561235857612358612459565b50601f01601f191660200190565b6000821982111561237957612379612417565b500190565b60008282101561239057612390612417565b500390565b60005b838110156123b0578181015183820152602001612398565b8381111561119b5750506000910152565b600181811c908216806123d557607f821691505b602082108114156123f657634e487b7160e01b600052602260045260246000fd5b50919050565b600060001982141561241057612410612417565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146114db57600080fd5b6001600160e01b0319811681146114db57600080fdfeddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122090b21747925068e2b95a809dfd2235f6a2462418c2d9737be1b7237b1f8bdc7764736f6c63430008060033

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

0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003782dace9d900000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000040519d946311449b33d6b03bb40892faeef12d75000000000000000000000000bd9aa94b28e6362fc610d485ce779ef6baa041aa000000000000000000000000f8ee8798f1fbd48f95ca70d2a68fdda404660b550000000000000000000000000000000000000000000000000000000000000006437970686572000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044350485200000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : args (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
16 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [2] : 00000000000000000000000000000000000000000000000000000000000001a0
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000400
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 00000000000000000000000000000000000000000000003782dace9d90000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [8] : 000000000000000000000000000000000000000000000000000000000000000e
Arg [9] : 00000000000000000000000040519d946311449b33d6b03bb40892faeef12d75
Arg [10] : 000000000000000000000000bd9aa94b28e6362fc610d485ce779ef6baa041aa
Arg [11] : 000000000000000000000000f8ee8798f1fbd48f95ca70d2a68fdda404660b55
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [13] : 4379706865720000000000000000000000000000000000000000000000000000
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [15] : 4350485200000000000000000000000000000000000000000000000000000000


Deployed Bytecode Sourcemap

89593:8303:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;36263:224;;;;;;;;;;-1:-1:-1;36263:224:0;;;;;:::i;:::-;;:::i;:::-;;;6675:14:1;;6668:22;6650:41;;6638:2;6623:18;36263:224:0;;;;;;;;92161:148;;;;;;;;;;-1:-1:-1;92161:148:0;;;;;:::i;:::-;;:::i;:::-;;23389:100;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;24948:221::-;;;;;;;;;;-1:-1:-1;24948:221:0;;;;;:::i;:::-;;:::i;:::-;;;-1:-1:-1;;;;;5973:32:1;;;5955:51;;5943:2;5928:18;24948:221:0;5910:102:1;92025:128:0;;;;;;;;;;-1:-1:-1;92025:128:0;;;;;:::i;:::-;;:::i;24471:411::-;;;;;;;;;;-1:-1:-1;24471:411:0;;;;;:::i;:::-;;:::i;36903:113::-;;;;;;;;;;-1:-1:-1;36991:10:0;:17;36903:113;;;7552:25:1;;;7540:2;7525:18;36903:113:0;7507:76:1;25838:339:0;;;;;;;;;;-1:-1:-1;25838:339:0;;;;;:::i;:::-;;:::i;91861:156::-;;;;;;;;;;-1:-1:-1;91861:156:0;;;;;:::i;:::-;;:::i;36571:256::-;;;;;;;;;;-1:-1:-1;36571:256:0;;;;;:::i;:::-;;:::i;92457:121::-;;;;;;;;;;-1:-1:-1;92457:121:0;;;;;:::i;:::-;;:::i;26248:185::-;;;;;;;;;;-1:-1:-1;26248:185:0;;;;;:::i;:::-;;:::i;96006:204::-;;;;;;;;;;-1:-1:-1;96006:204:0;;;;;:::i;:::-;;:::i;37093:233::-;;;;;;;;;;-1:-1:-1;37093:233:0;;;;;:::i;:::-;;:::i;92848:1256::-;;;;;;;;;;;;;:::i;:::-;;;;7058:14:1;;7051:22;7033:41;;7117:14;;7110:22;7105:2;7090:18;;7083:50;7149:18;;;7142:34;;;;7207:2;7192:18;;7185:34;;;;7250:3;7235:19;;7228:35;7294:3;7279:19;;7272:35;7338:3;7323:19;;7316:35;7382:3;7367:19;;7360:35;7020:3;7005:19;92848:1256:0;6987:414:1;23083:239:0;;;;;;;;;;-1:-1:-1;23083:239:0;;;;;:::i;:::-;;:::i;94112:1550::-;;;:::i;22813:208::-;;;;;;;;;;-1:-1:-1;22813:208:0;;;;;:::i;:::-;;:::i;3001:94::-;;;;;;;;;;;;;:::i;91689:164::-;;;;;;;;;;-1:-1:-1;91689:164:0;;;;;:::i;:::-;;:::i;2350:87::-;;;;;;;;;;;;;:::i;23558:104::-;;;;;;;;;;;;;:::i;25241:295::-;;;;;;;;;;-1:-1:-1;25241:295:0;;;;;:::i;:::-;;:::i;26504:328::-;;;;;;;;;;-1:-1:-1;26504:328:0;;;;;:::i;:::-;;:::i;92586:130::-;;;;;;;;;;-1:-1:-1;92586:130:0;;;;;:::i;:::-;;:::i;92317:132::-;;;;;;;;;;-1:-1:-1;92317:132:0;;;;;:::i;:::-;;:::i;96218:345::-;;;;;;;;;;-1:-1:-1;96218:345:0;;;;;:::i;:::-;;:::i;95671:327::-;;;;;;;;;;-1:-1:-1;95671:327:0;;;;;:::i;:::-;;:::i;92724:116::-;;;;;;;;;;-1:-1:-1;92724:116:0;;;;;:::i;:::-;;:::i;25607:164::-;;;;;;;;;;-1:-1:-1;25607:164:0;;;;;:::i;:::-;;:::i;91517:::-;;;;;;;;;;-1:-1:-1;91517:164:0;;;;;:::i;:::-;;:::i;3250:192::-;;;;;;;;;;-1:-1:-1;3250:192:0;;;;;:::i;:::-;;:::i;91436:73::-;;;;;;;;;;;;;:::i;36263:224::-;36365:4;-1:-1:-1;;;;;;36389:50:0;;-1:-1:-1;;;36389:50:0;;:90;;;36443:36;36467:11;36443:23;:36::i;:::-;36382:97;36263:224;-1:-1:-1;;36263:224:0:o;92161:148::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;;;;;;;;;91376:9:::1;::::0;::::1;;91375:10;91367:41;;;;-1:-1:-1::0;;;91367:41:0::1;;;;;;;:::i;:::-;92267:16:::2;:34:::0;92161:148::o;23389:100::-;23443:13;23476:5;23469:12;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23389:100;:::o;24948:221::-;25024:7;25052:16;25060:7;25052;:16::i;:::-;25044:73;;;;-1:-1:-1;;;25044:73:0;;13099:2:1;25044:73:0;;;13081:21:1;13138:2;13118:18;;;13111:30;13177:34;13157:18;;;13150:62;-1:-1:-1;;;13228:18:1;;;13221:42;13280:19;;25044:73:0;13071:234:1;25044:73:0;-1:-1:-1;25137:24:0;;;;:15;:24;;;;;;-1:-1:-1;;;;;25137:24:0;;24948:221::o;92025:128::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;91376:9:::1;::::0;::::1;;91375:10;91367:41;;;;-1:-1:-1::0;;;91367:41:0::1;;;;;;;:::i;:::-;92121:11:::2;:24:::0;92025:128::o;24471:411::-;24552:13;24568:23;24583:7;24568:14;:23::i;:::-;24552:39;;24616:5;-1:-1:-1;;;;;24610:11:0;:2;-1:-1:-1;;;;;24610:11:0;;;24602:57;;;;-1:-1:-1;;;24602:57:0;;14638:2:1;24602:57:0;;;14620:21:1;14677:2;14657:18;;;14650:30;14716:34;14696:18;;;14689:62;-1:-1:-1;;;14767:18:1;;;14760:31;14808:19;;24602:57:0;14610:223:1;24602:57:0;1304:10;-1:-1:-1;;;;;24694:21:0;;;;:62;;-1:-1:-1;24719:37:0;24736:5;1304:10;25607:164;:::i;24719:37::-;24672:168;;;;-1:-1:-1;;;24672:168:0;;11492:2:1;24672:168:0;;;11474:21:1;11531:2;11511:18;;;11504:30;11570:34;11550:18;;;11543:62;-1:-1:-1;;;11621:18:1;;;11614:54;11685:19;;24672:168:0;11464:246:1;24672:168:0;24853:21;24862:2;24866:7;24853:8;:21::i;:::-;24541:341;24471:411;;:::o;25838:339::-;26033:41;1304:10;26066:7;26033:18;:41::i;:::-;26025:103;;;;-1:-1:-1;;;26025:103:0;;;;;;;:::i;:::-;26141:28;26151:4;26157:2;26161:7;26141:9;:28::i;91861:156::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;91376:9:::1;::::0;::::1;;91375:10;91367:41;;;;-1:-1:-1::0;;;91367:41:0::1;;;;;;;:::i;:::-;91971:18:::2;:38:::0;91861:156::o;36571:256::-;36668:7;36704:23;36721:5;36704:16;:23::i;:::-;36696:5;:31;36688:87;;;;-1:-1:-1;;;36688:87:0;;8014:2:1;36688:87:0;;;7996:21:1;8053:2;8033:18;;;8026:30;8092:34;8072:18;;;8065:62;-1:-1:-1;;;8143:18:1;;;8136:41;8194:19;;36688:87:0;7986:233:1;36688:87:0;-1:-1:-1;;;;;;36793:19:0;;;;;;;;:12;:19;;;;;;;;:26;;;;;;;;;36571:256::o;92457:121::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;92548:10:::1;:22:::0;;-1:-1:-1;;;;;;92548:22:0::1;-1:-1:-1::0;;;;;92548:22:0;;;::::1;::::0;;;::::1;::::0;;92457:121::o;26248:185::-;26386:39;26403:4;26409:2;26413:7;26386:39;;;;;;;;;;;;:16;:39::i;96006:204::-;96072:13;96112:16;96120:7;96112;:16::i;:::-;96104:45;;;;-1:-1:-1;;;96104:45:0;;;;;;;:::i;:::-;96167:8;;;96185:16;;;:7;:16;;;;;;;;96167:35;;-1:-1:-1;;;96167:35:0;;-1:-1:-1;;;;;96167:8:0;;;;:17;;:35;;;;7552:25:1;;;7540:2;7525:18;;7507:76;96167:35:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;96167:35:0;;;;;;;;;;;;:::i;37093:233::-;37168:7;37204:30;36991:10;:17;;36903:113;37204:30;37196:5;:38;37188:95;;;;-1:-1:-1;;;37188:95:0;;16148:2:1;37188:95:0;;;16130:21:1;16187:2;16167:18;;;16160:30;16226:34;16206:18;;;16199:62;-1:-1:-1;;;16277:18:1;;;16270:42;16329:19;;37188:95:0;16120:234:1;37188:95:0;37301:10;37312:5;37301:17;;;;;;;;:::i;:::-;;;;;;;;;37294:24;;37093:233;;;:::o;92848:1256::-;92910:15;;;;;;;;;93242:16;:12;93257:1;93242:16;:::i;:::-;93222:36;;93286:18;;93273:9;:31;93269:295;;93343:18;;:23;;;-1:-1:-1;93365:1:0;;-1:-1:-1;93269:295:0;;;93474:5;93461:18;;93543:9;93522:18;;:30;;;;:::i;:::-;93496:56;;93269:295;93580:16;;93576:417;;93638:5;93627:16;;93746:27;93763:9;93746:16;:27::i;:::-;93660:113;;-1:-1:-1;93660:113:0;-1:-1:-1;93660:113:0;-1:-1:-1;93576:417:0;;;93835:4;93824:15;;93906:34;93923:16;;93906;:34::i;:::-;-1:-1:-1;93856:84:0;;-1:-1:-1;93856:84:0;-1:-1:-1;93980:1:0;;-1:-1:-1;93576:417:0;36991:10;:17;94005:38;;94076:20;;94054:42;;93211:893;92848:1256;;;;;;;;:::o;23083:239::-;23155:7;23191:16;;;:7;:16;;;;;;-1:-1:-1;;;;;23191:16:0;23226:19;23218:73;;;;-1:-1:-1;;;23218:73:0;;12328:2:1;23218:73:0;;;12310:21:1;12367:2;12347:18;;;12340:30;12406:34;12386:18;;;12379:62;-1:-1:-1;;;12457:18:1;;;12450:39;12506:19;;23218:73:0;12300:231:1;94112:1550:0;94166:15;94228:1;94207:18;;:22;:60;;;;;94249:18;;94233:12;:34;;94207:60;94199:117;;;;-1:-1:-1;;;94199:117:0;;13873:2:1;94199:117:0;;;13855:21:1;13912:2;13892:18;;;13885:30;-1:-1:-1;;;13931:18:1;;;13924:56;13997:18;;94199:117:0;13845:176:1;94199:117:0;94329:24;94356:13;36991:10;:17;;36903:113;94356:13;94329:40;;94380:28;94434:16;94411:20;;:39;;;;:::i;:::-;94380:70;;94502:20;;94479;:43;:111;;;;94563:1;94540:20;:24;:49;;;;;94582:7;:5;:7::i;:::-;-1:-1:-1;;;;;94568:21:0;:10;-1:-1:-1;;;;;94568:21:0;;94540:49;94471:167;;;;-1:-1:-1;;;94471:167:0;;9609:2:1;94471:167:0;;;9591:21:1;9648:2;9628:18;;;9621:30;9687:31;9667:18;;;9660:59;9736:18;;94471:167:0;9581:179:1;94471:167:0;94700:16;;94673:12;;94700:21;94696:231;;-1:-1:-1;94761:16:0;;94696:231;;;94833:20;;:24;;94856:1;94833:24;:::i;:::-;94808:20;:50;94804:123;;;94903:12;94884:16;:31;94804:123;94939:18;94968:11;95012:29;95029:11;95012:16;:29::i;:::-;-1:-1:-1;94990:51:0;;-1:-1:-1;94990:51:0;-1:-1:-1;95062:9:0;:16;;95054:53;;;;-1:-1:-1;;;95054:53:0;;10726:2:1;95054:53:0;;;10708:21:1;10765:2;10745:18;;;10738:30;-1:-1:-1;;;10784:18:1;;;10777:54;10848:18;;95054:53:0;10698:174:1;95054:53:0;95147:10;;:36;;95129:12;;-1:-1:-1;;;;;95147:10:0;;95169:9;;95129:12;95147:36;95129:12;95147:36;95169:9;95147:10;:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;95128:55;;;95202:7;95194:36;;;;-1:-1:-1;;;95194:36:0;;15040:2:1;95194:36:0;;;15022:21:1;15079:2;15059:18;;;15052:30;-1:-1:-1;;;15098:18:1;;;15091:46;15154:18;;95194:36:0;15012:166:1;95194:36:0;95261:16;95251:26;;95288:12;95344:7;95408:1;95393:12;:16;;;;:::i;:::-;95383:27;95451:1;95441:7;:11;:47;;95486:1;95441:47;;;95455:7;:20;95463:11;95473:1;95463:7;:11;:::i;:::-;95455:20;;;;;;;;;;;;95441:47;95327:162;;;;;;5677:19:1;;;;5712:12;;5705:28;;;;5749:12;;;5742:28;5786:12;;95327:162:0;;;-1:-1:-1;;95327:162:0;;;;;;;;;95303:187;;95327:162;95303:187;;;;95511:16;;;;:7;:16;;;;;:23;;;95545:11;:20;;;;;:33;;;95303:187;-1:-1:-1;95589:30:0;95599:10;95519:7;95589:9;:30::i;:::-;95640:14;;;;;;;94112:1550;:::o;22813:208::-;22885:7;-1:-1:-1;;;;;22913:19:0;;22905:74;;;;-1:-1:-1;;;22905:74:0;;11917:2:1;22905:74:0;;;11899:21:1;11956:2;11936:18;;;11929:30;11995:34;11975:18;;;11968:62;-1:-1:-1;;;12046:18:1;;;12039:40;12096:19;;22905:74:0;11889:232:1;22905:74:0;-1:-1:-1;;;;;;22997:16:0;;;;;:9;:16;;;;;;;22813:208::o;3001:94::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;3066:21:::1;3084:1;3066:9;:21::i;:::-;3001:94::o:0;91689:164::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;91376:9:::1;::::0;::::1;;91375:10;91367:41;;;;-1:-1:-1::0;;;91367:41:0::1;;;;;;;:::i;:::-;91803:20:::2;:42:::0;91689:164::o;2350:87::-;2423:6;;-1:-1:-1;;;;;2423:6:0;;2350:87::o;23558:104::-;23614:13;23647:7;23640:14;;;;;:::i;25241:295::-;-1:-1:-1;;;;;25344:24:0;;1304:10;25344:24;;25336:62;;;;-1:-1:-1;;;25336:62:0;;10372:2:1;25336:62:0;;;10354:21:1;10411:2;10391:18;;;10384:30;-1:-1:-1;;;10430:18:1;;;10423:55;10495:18;;25336:62:0;10344:175:1;25336:62:0;1304:10;25411:32;;;;:18;:32;;;;;;;;-1:-1:-1;;;;;25411:42:0;;;;;;;;;;;;:53;;-1:-1:-1;;25411:53:0;;;;;;;;;;25480:48;;6650:41:1;;;25411:42:0;;1304:10;25480:48;;6623:18:1;25480:48:0;;;;;;;25241:295;;:::o;26504:328::-;26679:41;1304:10;26712:7;26679:18;:41::i;:::-;26671:103;;;;-1:-1:-1;;;26671:103:0;;;;;;;:::i;:::-;26785:39;26799:4;26805:2;26809:7;26818:5;26785:13;:39::i;:::-;26504:328;;;;:::o;92586:130::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;91376:9:::1;::::0;::::1;;91375:10;91367:41;;;;-1:-1:-1::0;;;91367:41:0::1;;;;;;;:::i;:::-;92690:8:::2;:18:::0;;-1:-1:-1;;;;;;92690:18:0::2;-1:-1:-1::0;;;;;92690:18:0;;;::::2;::::0;;;::::2;::::0;;92586:130::o;92317:132::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;91376:9:::1;::::0;::::1;;91375:10;91367:41;;;;-1:-1:-1::0;;;91367:41:0::1;;;;;;;:::i;:::-;92415:12:::2;:26:::0;92317:132::o;96218:345::-;96301:13;96341:16;96349:7;96341;:16::i;:::-;96333:45;;;;-1:-1:-1;;;96333:45:0;;;;;;;:::i;:::-;96398:9;;;96454:16;;;:7;:16;;;;;;;;;96485:11;:20;;;;;;;-1:-1:-1;;;;;96398:9:0;;;;:18;;96431:7;;96520:34;96431:7;96520:25;:34::i;:::-;96398:157;;-1:-1:-1;;;;;;96398:157:0;;;;;;;;;;17113:25:1;;;;17154:18;;;17147:34;;;;17197:18;;;17190:34;17267:14;17260:22;17240:18;;;17233:50;17085:19;;96398:157:0;17067:222:1;95671:327:0;95736:13;95775:16;95783:7;95775;:16::i;:::-;95767:45;;;;-1:-1:-1;;;95767:45:0;;;;;;;:::i;:::-;95832:9;;;95889:16;;;:7;:16;;;;;;;;;95920:11;:20;;;;;;;-1:-1:-1;;;;;95832:9:0;;;;:18;;95866:7;;95955:34;95866:7;95955:25;:34::i;92724:116::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;92816:9:::1;:16:::0;;-1:-1:-1;;;;;;92816:16:0::1;-1:-1:-1::0;;;;;92816:16:0;;;::::1;::::0;;;::::1;::::0;;92724:116::o;25607:164::-;-1:-1:-1;;;;;25728:25:0;;;25704:4;25728:25;;;:18;:25;;;;;;;;:35;;;;;;;;;;;;;;;25607:164::o;91517:::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;91376:9:::1;::::0;::::1;;91375:10;91367:41;;;;-1:-1:-1::0;;;91367:41:0::1;;;;;;;:::i;:::-;91631:20:::2;:42:::0;91517:164::o;3250:192::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;-1:-1:-1;;;;;3339:22:0;::::1;3331:73;;;::::0;-1:-1:-1;;;3331:73:0;;8845:2:1;3331:73:0::1;::::0;::::1;8827:21:1::0;8884:2;8864:18;;;8857:30;8923:34;8903:18;;;8896:62;-1:-1:-1;;;8974:18:1;;;8967:36;9020:19;;3331:73:0::1;8817:228:1::0;3331:73:0::1;3415:19;3425:8;3415:9;:19::i;:::-;3250:192:::0;:::o;91436:73::-;1304:10;2570:7;:5;:7::i;:::-;-1:-1:-1;;;;;2570:23:0;;2562:68;;;;-1:-1:-1;;;2562:68:0;;;;;;;:::i;:::-;91485:9:::1;:16:::0;;-1:-1:-1;;91485:16:0::1;91497:4;91485:16;::::0;;91436:73::o;12112:387::-;12435:20;12483:8;;;12112:387::o;22444:305::-;22546:4;-1:-1:-1;;;;;;22583:40:0;;-1:-1:-1;;;22583:40:0;;:105;;-1:-1:-1;;;;;;;22640:48:0;;-1:-1:-1;;;22640:48:0;22583:105;:158;;;-1:-1:-1;;;;;;;;;;9896:40:0;;;22705:36;9787:157;28342:127;28407:4;28431:16;;;:7;:16;;;;;;-1:-1:-1;;;;;28431:16:0;:30;;;28342:127::o;32324:174::-;32399:24;;;;:15;:24;;;;;:29;;-1:-1:-1;;;;;;32399:29:0;-1:-1:-1;;;;;32399:29:0;;;;;;;;:24;;32453:23;32399:24;32453:14;:23::i;:::-;-1:-1:-1;;;;;32444:46:0;;;;;;;;;;;32324:174;;:::o;28636:348::-;28729:4;28754:16;28762:7;28754;:16::i;:::-;28746:73;;;;-1:-1:-1;;;28746:73:0;;11079:2:1;28746:73:0;;;11061:21:1;11118:2;11098:18;;;11091:30;11157:34;11137:18;;;11130:62;-1:-1:-1;;;11208:18:1;;;11201:42;11260:19;;28746:73:0;11051:234:1;28746:73:0;28830:13;28846:23;28861:7;28846:14;:23::i;:::-;28830:39;;28899:5;-1:-1:-1;;;;;28888:16:0;:7;-1:-1:-1;;;;;28888:16:0;;:51;;;;28932:7;-1:-1:-1;;;;;28908:31:0;:20;28920:7;28908:11;:20::i;:::-;-1:-1:-1;;;;;28908:31:0;;28888:51;:87;;;;28943:32;28960:5;28967:7;28943:16;:32::i;:::-;28880:96;28636:348;-1:-1:-1;;;;28636:348:0:o;31628:578::-;31787:4;-1:-1:-1;;;;;31760:31:0;:23;31775:7;31760:14;:23::i;:::-;-1:-1:-1;;;;;31760:31:0;;31752:85;;;;-1:-1:-1;;;31752:85:0;;14228:2:1;31752:85:0;;;14210:21:1;14267:2;14247:18;;;14240:30;14306:34;14286:18;;;14279:62;-1:-1:-1;;;14357:18:1;;;14350:39;14406:19;;31752:85:0;14200:231:1;31752:85:0;-1:-1:-1;;;;;31856:16:0;;31848:65;;;;-1:-1:-1;;;31848:65:0;;9967:2:1;31848:65:0;;;9949:21:1;10006:2;9986:18;;;9979:30;10045:34;10025:18;;;10018:62;-1:-1:-1;;;10096:18:1;;;10089:34;10140:19;;31848:65:0;9939:226:1;31848:65:0;31926:39;31947:4;31953:2;31957:7;31926:20;:39::i;:::-;32030:29;32047:1;32051:7;32030:8;:29::i;:::-;-1:-1:-1;;;;;32072:15:0;;;;;;:9;:15;;;;;:20;;32091:1;;32072:15;:20;;32091:1;;32072:20;:::i;:::-;;;;-1:-1:-1;;;;;;;32103:13:0;;;;;;:9;:13;;;;;:18;;32120:1;;32103:13;:18;;32120:1;;32103:18;:::i;:::-;;;;-1:-1:-1;;32132:16:0;;;;:7;:16;;;;;;:21;;-1:-1:-1;;;;;;32132:21:0;-1:-1:-1;;;;;32132:21:0;;;;;;;;;32171:27;;32132:16;;32171:27;;;;-1:-1:-1;;;;;;;;;;;32171:27:0;;31628:578;;;:::o;96571:1071::-;96650:25;96687:18;96717:30;96769:18;;96791:1;96769:23;96765:91;;;-1:-1:-1;;96829:11:0;;96826:1;;-1:-1:-1;96826:1:0;96818:26;;96765:91;96894:18;;96880:11;:32;96876:200;;;96938:20;96982:16;;96961:18;;:37;;;;:::i;:::-;96938:60;;97021:1;97024:11;;97052;97037:12;:26;;;;:::i;:::-;97013:51;;;;;;;;;96876:200;97135:11;;97176:16;;97225:18;;97088;;97135:11;97176:16;97088:18;;97225:29;;97176:16;;97225:29;:::i;:::-;97203:51;;97272:9;97267:328;97291:12;;97287:1;:16;97267:328;;;97352:11;97338;:25;97334:130;;;97405:10;97417:3;97422:25;97436:11;97422;:25;:::i;:::-;97397:51;;;;;;;;;;;;;97334:130;97480:12;;;:::i;:::-;;-1:-1:-1;97515:1:0;97507:9;;;;97531:14;;;;;97560:23;97531:14;97560:23;;:::i;:::-;;-1:-1:-1;97305:3:0;;;:::i;:::-;;;97267:328;;;-1:-1:-1;97615:10:0;;-1:-1:-1;97627:3:0;;-1:-1:-1;97632:1:0;;-1:-1:-1;;;96571:1071:0;;;;;;:::o;29326:110::-;29402:26;29412:2;29416:7;29402:26;;;;;;;;;;;;:9;:26::i;:::-;29326:110;;:::o;3450:173::-;3525:6;;;-1:-1:-1;;;;;3542:17:0;;;-1:-1:-1;;;;;;3542:17:0;;;;;;;3575:40;;3525:6;;;3542:17;3525:6;;3575:40;;3506:16;;3575:40;3495:128;3450:173;:::o;27714:315::-;27871:28;27881:4;27887:2;27891:7;27871:9;:28::i;:::-;27918:48;27941:4;27947:2;27951:7;27960:5;27918:22;:48::i;:::-;27910:111;;;;-1:-1:-1;;;27910:111:0;;;;;;;:::i;97650:243::-;97732:4;97758:11;;97754:108;;97826:11;:24;97838:11;97848:1;97838:7;:11;:::i;:::-;97826:24;;;;;;;;;;;;97802:11;:20;97814:7;97802:20;;;;;;;;;;;;:48;;97795:55;;97650:243;;;:::o;97754:108::-;-1:-1:-1;97881:4:0;;97650:243;-1:-1:-1;97650:243:0:o;37939:589::-;-1:-1:-1;;;;;38145:18:0;;38141:187;;38180:40;38212:7;39355:10;:17;;39328:24;;;;:15;:24;;;;;:44;;;39383:24;;;;;;;;;;;;39251:164;38180:40;38141:187;;;38250:2;-1:-1:-1;;;;;38242:10:0;:4;-1:-1:-1;;;;;38242:10:0;;38238:90;;38269:47;38302:4;38308:7;38269:32;:47::i;:::-;-1:-1:-1;;;;;38342:16:0;;38338:183;;38375:45;38412:7;38375:36;:45::i;38338:183::-;38448:4;-1:-1:-1;;;;;38442:10:0;:2;-1:-1:-1;;;;;38442:10:0;;38438:83;;38469:40;38497:2;38501:7;38469:27;:40::i;29663:321::-;29793:18;29799:2;29803:7;29793:5;:18::i;:::-;29844:54;29875:1;29879:2;29883:7;29892:5;29844:22;:54::i;:::-;29822:154;;;;-1:-1:-1;;;29822:154:0;;;;;;;:::i;33063:803::-;33218:4;-1:-1:-1;;;;;33239:13:0;;12435:20;12483:8;33235:624;;33275:72;;-1:-1:-1;;;33275:72:0;;-1:-1:-1;;;;;33275:36:0;;;;;:72;;1304:10;;33326:4;;33332:7;;33341:5;;33275:72;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;33275:72:0;;;;;;;;-1:-1:-1;;33275:72:0;;;;;;;;;;;;:::i;:::-;;;33271:533;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;33521:13:0;;33517:272;;33564:60;;-1:-1:-1;;;33564:60:0;;;;;;;:::i;33517:272::-;33739:6;33733:13;33724:6;33720:2;33716:15;33709:38;33271:533;-1:-1:-1;;;;;;33398:55:0;-1:-1:-1;;;33398:55:0;;-1:-1:-1;33391:62:0;;33235:624;-1:-1:-1;33843:4:0;33063:803;;;;;;:::o;40042:988::-;40308:22;40358:1;40333:22;40350:4;40333:16;:22::i;:::-;:26;;;;:::i;:::-;40370:18;40391:26;;;:17;:26;;;;;;40308:51;;-1:-1:-1;40524:28:0;;;40520:328;;-1:-1:-1;;;;;40591:18:0;;40569:19;40591:18;;;:12;:18;;;;;;;;:34;;;;;;;;;40642:30;;;;;;:44;;;40759:30;;:17;:30;;;;;:43;;;40520:328;-1:-1:-1;40944:26:0;;;;:17;:26;;;;;;;;40937:33;;;-1:-1:-1;;;;;40988:18:0;;;;;:12;:18;;;;;:34;;;;;;;40981:41;40042:988::o;41325:1079::-;41603:10;:17;41578:22;;41603:21;;41623:1;;41603:21;:::i;:::-;41635:18;41656:24;;;:15;:24;;;;;;42029:10;:26;;41578:46;;-1:-1:-1;41656:24:0;;41578:46;;42029:26;;;;;;:::i;:::-;;;;;;;;;42007:48;;42093:11;42068:10;42079;42068:22;;;;;;;;:::i;:::-;;;;;;;;;;;;:36;;;;42173:28;;;:15;:28;;;;;;;:41;;;42345:24;;;;;42338:31;42380:10;:16;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;41396:1008;;;41325:1079;:::o;38829:221::-;38914:14;38931:20;38948:2;38931:16;:20::i;:::-;-1:-1:-1;;;;;38962:16:0;;;;;;;:12;:16;;;;;;;;:24;;;;;;;;:34;;;39007:26;;;:17;:26;;;;;;:35;;;;-1:-1:-1;38829:221:0:o;30320:382::-;-1:-1:-1;;;;;30400:16:0;;30392:61;;;;-1:-1:-1;;;30392:61:0;;12738:2:1;30392:61:0;;;12720:21:1;;;12757:18;;;12750:30;12816:34;12796:18;;;12789:62;12868:18;;30392:61:0;12710:182:1;30392:61:0;30473:16;30481:7;30473;:16::i;:::-;30472:17;30464:58;;;;-1:-1:-1;;;30464:58:0;;9252:2:1;30464:58:0;;;9234:21:1;9291:2;9271:18;;;9264:30;-1:-1:-1;;;9310:18:1;;;9303:58;9378:18;;30464:58:0;9224:178:1;30464:58:0;30535:45;30564:1;30568:2;30572:7;30535:20;:45::i;:::-;-1:-1:-1;;;;;30593:13:0;;;;;;:9;:13;;;;;:18;;30610:1;;30593:13;:18;;30610:1;;30593:18;:::i;:::-;;;;-1:-1:-1;;30622:16:0;;;;:7;:16;;;;;;:21;;-1:-1:-1;;;;;;30622:21:0;-1:-1:-1;;;;;30622:21:0;;;;;;;;30661:33;;30622:16;;;-1:-1:-1;;;;;;;;;;;30661:33:0;30622:16;;30661:33;30320:382;;:::o;14:247:1:-;73:6;126:2;114:9;105:7;101:23;97:32;94:2;;;142:1;139;132:12;94:2;181:9;168:23;200:31;225:5;200:31;:::i;:::-;250:5;84:177;-1:-1:-1;;;84:177:1:o;526:388::-;594:6;602;655:2;643:9;634:7;630:23;626:32;623:2;;;671:1;668;661:12;623:2;710:9;697:23;729:31;754:5;729:31;:::i;:::-;779:5;-1:-1:-1;836:2:1;821:18;;808:32;849:33;808:32;849:33;:::i;:::-;901:7;891:17;;;613:301;;;;;:::o;919:456::-;996:6;1004;1012;1065:2;1053:9;1044:7;1040:23;1036:32;1033:2;;;1081:1;1078;1071:12;1033:2;1120:9;1107:23;1139:31;1164:5;1139:31;:::i;:::-;1189:5;-1:-1:-1;1246:2:1;1231:18;;1218:32;1259:33;1218:32;1259:33;:::i;:::-;1023:352;;1311:7;;-1:-1:-1;;;1365:2:1;1350:18;;;;1337:32;;1023:352::o;1380:1016::-;1475:6;1483;1491;1499;1552:3;1540:9;1531:7;1527:23;1523:33;1520:2;;;1569:1;1566;1559:12;1520:2;1608:9;1595:23;1627:31;1652:5;1627:31;:::i;:::-;1677:5;-1:-1:-1;1734:2:1;1719:18;;1706:32;1747:33;1706:32;1747:33;:::i;:::-;1799:7;-1:-1:-1;1853:2:1;1838:18;;1825:32;;-1:-1:-1;1908:2:1;1893:18;;1880:32;-1:-1:-1;;;;;1924:30:1;;1921:2;;;1967:1;1964;1957:12;1921:2;1990:22;;2043:4;2035:13;;2031:27;-1:-1:-1;2021:2:1;;2072:1;2069;2062:12;2021:2;2108;2095:16;2133:48;2149:31;2177:2;2149:31;:::i;:::-;2133:48;:::i;:::-;2204:2;2197:5;2190:17;2244:7;2239:2;2234;2230;2226:11;2222:20;2219:33;2216:2;;;2265:1;2262;2255:12;2216:2;2320;2315;2311;2307:11;2302:2;2295:5;2291:14;2278:45;2364:1;2359:2;2354;2347:5;2343:14;2339:23;2332:34;2385:5;2375:15;;;;;1510:886;;;;;;;:::o;2401:416::-;2466:6;2474;2527:2;2515:9;2506:7;2502:23;2498:32;2495:2;;;2543:1;2540;2533:12;2495:2;2582:9;2569:23;2601:31;2626:5;2601:31;:::i;:::-;2651:5;-1:-1:-1;2708:2:1;2693:18;;2680:32;2750:15;;2743:23;2731:36;;2721:2;;2781:1;2778;2771:12;2822:315;2890:6;2898;2951:2;2939:9;2930:7;2926:23;2922:32;2919:2;;;2967:1;2964;2957:12;2919:2;3006:9;2993:23;3025:31;3050:5;3025:31;:::i;:::-;3075:5;3127:2;3112:18;;;;3099:32;;-1:-1:-1;;;2909:228:1:o;3142:245::-;3200:6;3253:2;3241:9;3232:7;3228:23;3224:32;3221:2;;;3269:1;3266;3259:12;3221:2;3308:9;3295:23;3327:30;3351:5;3327:30;:::i;3392:249::-;3461:6;3514:2;3502:9;3493:7;3489:23;3485:32;3482:2;;;3530:1;3527;3520:12;3482:2;3562:9;3556:16;3581:30;3605:5;3581:30;:::i;4195:635::-;4275:6;4328:2;4316:9;4307:7;4303:23;4299:32;4296:2;;;4344:1;4341;4334:12;4296:2;4371:16;;-1:-1:-1;;;;;4399:30:1;;4396:2;;;4442:1;4439;4432:12;4396:2;4465:22;;4518:4;4510:13;;4506:27;-1:-1:-1;4496:2:1;;4547:1;4544;4537:12;4496:2;4576;4570:9;4601:48;4617:31;4645:2;4617:31;:::i;4601:48::-;4672:2;4665:5;4658:17;4712:7;4707:2;4702;4698;4694:11;4690:20;4687:33;4684:2;;;4733:1;4730;4723:12;4684:2;4746:54;4797:2;4792;4785:5;4781:14;4776:2;4772;4768:11;4746:54;:::i;:::-;4819:5;4286:544;-1:-1:-1;;;;;4286:544:1:o;4835:180::-;4894:6;4947:2;4935:9;4926:7;4922:23;4918:32;4915:2;;;4963:1;4960;4953:12;4915:2;-1:-1:-1;4986:23:1;;4905:110;-1:-1:-1;4905:110:1:o;5020:257::-;5061:3;5099:5;5093:12;5126:6;5121:3;5114:19;5142:63;5198:6;5191:4;5186:3;5182:14;5175:4;5168:5;5164:16;5142:63;:::i;:::-;5259:2;5238:15;-1:-1:-1;;5234:29:1;5225:39;;;;5266:4;5221:50;;5069:208;-1:-1:-1;;5069:208:1:o;6017:488::-;-1:-1:-1;;;;;6286:15:1;;;6268:34;;6338:15;;6333:2;6318:18;;6311:43;6385:2;6370:18;;6363:34;;;6433:3;6428:2;6413:18;;6406:31;;;6211:4;;6454:45;;6479:19;;6471:6;6454:45;:::i;:::-;6446:53;6220:285;-1:-1:-1;;;;;;6220:285:1:o;7588:219::-;7737:2;7726:9;7719:21;7700:4;7757:44;7797:2;7786:9;7782:18;7774:6;7757:44;:::i;8224:414::-;8426:2;8408:21;;;8465:2;8445:18;;;8438:30;8504:34;8499:2;8484:18;;8477:62;-1:-1:-1;;;8570:2:1;8555:18;;8548:48;8628:3;8613:19;;8398:240::o;13310:356::-;13512:2;13494:21;;;13531:18;;;13524:30;13590:34;13585:2;13570:18;;13563:62;13657:2;13642:18;;13484:182::o;15183:413::-;15385:2;15367:21;;;15424:2;15404:18;;;15397:30;15463:34;15458:2;15443:18;;15436:62;-1:-1:-1;;;15529:2:1;15514:18;;15507:47;15586:3;15571:19;;15357:239::o;15601:340::-;15803:2;15785:21;;;15842:2;15822:18;;;15815:30;-1:-1:-1;;;15876:2:1;15861:18;;15854:46;15932:2;15917:18;;15775:166::o;16359:342::-;16561:2;16543:21;;;16600:2;16580:18;;;16573:30;-1:-1:-1;;;16634:2:1;16619:18;;16612:48;16692:2;16677:18;;16533:168::o;17294:275::-;17365:2;17359:9;17430:2;17411:13;;-1:-1:-1;;17407:27:1;17395:40;;-1:-1:-1;;;;;17450:34:1;;17486:22;;;17447:62;17444:2;;;17512:18;;:::i;:::-;17548:2;17541:22;17339:230;;-1:-1:-1;17339:230:1:o;17574:186::-;17622:4;-1:-1:-1;;;;;17644:30:1;;17641:2;;;17677:18;;:::i;:::-;-1:-1:-1;17743:2:1;17722:15;-1:-1:-1;;17718:29:1;17749:4;17714:40;;17631:129::o;17765:128::-;17805:3;17836:1;17832:6;17829:1;17826:13;17823:2;;;17842:18;;:::i;:::-;-1:-1:-1;17878:9:1;;17813:80::o;17898:125::-;17938:4;17966:1;17963;17960:8;17957:2;;;17971:18;;:::i;:::-;-1:-1:-1;18008:9:1;;17947:76::o;18028:258::-;18100:1;18110:113;18124:6;18121:1;18118:13;18110:113;;;18200:11;;;18194:18;18181:11;;;18174:39;18146:2;18139:10;18110:113;;;18241:6;18238:1;18235:13;18232:2;;;-1:-1:-1;;18276:1:1;18258:16;;18251:27;18081:205::o;18291:380::-;18370:1;18366:12;;;;18413;;;18434:2;;18488:4;18480:6;18476:17;18466:27;;18434:2;18541;18533:6;18530:14;18510:18;18507:38;18504:2;;;18587:10;18582:3;18578:20;18575:1;18568:31;18622:4;18619:1;18612:15;18650:4;18647:1;18640:15;18504:2;;18346:325;;;:::o;18676:135::-;18715:3;-1:-1:-1;;18736:17:1;;18733:2;;;18756:18;;:::i;:::-;-1:-1:-1;18803:1:1;18792:13;;18723:88::o;18816:127::-;18877:10;18872:3;18868:20;18865:1;18858:31;18908:4;18905:1;18898:15;18932:4;18929:1;18922:15;18948:127;19009:10;19004:3;19000:20;18997:1;18990:31;19040:4;19037:1;19030:15;19064:4;19061:1;19054:15;19080:127;19141:10;19136:3;19132:20;19129:1;19122:31;19172:4;19169:1;19162:15;19196:4;19193:1;19186:15;19212:127;19273:10;19268:3;19264:20;19261:1;19254:31;19304:4;19301:1;19294:15;19328:4;19325:1;19318:15;19344:131;-1:-1:-1;;;;;19419:31:1;;19409:42;;19399:2;;19465:1;19462;19455:12;19480:131;-1:-1:-1;;;;;;19554:32:1;;19544:43;;19534:2;;19601:1;19598;19591:12

Swarm Source

ipfs://90b21747925068e2b95a809dfd2235f6a2462418c2d9737be1b7237b1f8bdc77
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.