APE Price: $1.48 (-6.68%)

Contract

0xBb4C0f8E850076e2e83C74Cf168AB5786120cd45

Overview

APE Balance

Apechain LogoApechain LogoApechain Logo0 APE

APE Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CollectionMetadataRenderer

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
File 1 of 14 : CollectionMetadataRenderer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {StringsUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
import {IERC721MetadataUpgradeable} from "@openzeppelin/contracts-upgradeable/interfaces/IERC721MetadataUpgradeable.sol";
import {IMetadataRenderer} from "../interfaces/IMetadataRenderer.sol";
import {DynamicMetadataRenderer} from "./DynamicMetadataRenderer.sol";
import {MetadataRenderAdminCheck} from "./MetadataRenderAdminCheck.sol";

/// @notice Collection metadata system
contract CollectionMetadataRenderer is IMetadataRenderer, MetadataRenderAdminCheck {
    error MetadataFrozen();

    /// Event to mark updated metadata information
    event MetadataUpdated(address indexed target, string metadataBase, string metadataExtension, string contractURI, uint256 freezeAt);

    event DynamicMetadataUpdated(address indexed target, string imageURI, string animationURI);

    /// @notice Hash to mark updated provenance hash
    event ProvenanceHashUpdated(address indexed target, bytes32 provenanceHash);

    /// @notice Struct to store metadata info and update data
    struct MetadataURIInfo {
        string base;
        string extension;
        string contractURI;
        uint256 freezeAt;
    }

    struct DynamicMetadataInfo {
        string description;
        string imageURI;
        string animationURI;
    }

    /// @notice NFT metadata by contract
    mapping(address => MetadataURIInfo) public metadataBaseByContract;

    /// @notice Dynamic NFT metadata by contract
    mapping(address => DynamicMetadataInfo) public dynamicMetadataInfoByContract;

    /// @notice Optional provenance hashes for NFT metadata by contract
    mapping(address => bytes32) public provenanceHashes;

    /// @notice Standard init for collection metadata from root collection contract
    /// @param metadataBase passed in for initialization
    /// @param dynamicTokenData passed in for initialization
    function initializeWithData(bytes memory metadataBase, bytes memory dynamicTokenData) external {
        if (metadataBase.length > 0) {
            (string memory initialBaseURI, string memory initialContractURI) = abi.decode(metadataBase, (string, string));
            _updateMetadataDetails(msg.sender, initialBaseURI, "", initialContractURI, 0);
        }

        if (dynamicTokenData.length > 0) {
            (string memory description, string memory imageURI, string memory animationURI) = abi.decode(dynamicTokenData, (string, string, string));
            _updateDynamicMetadataInfo(msg.sender, description, imageURI, animationURI);            
        }
    }

    /// @notice Update the provenance hash (optional) for a given nft
    /// @param target target address to update
    /// @param provenanceHash provenance hash to set
    function updateProvenanceHash(address target, bytes32 provenanceHash) external requireSenderAdmin(target) {
        provenanceHashes[target] = provenanceHash;
        emit ProvenanceHashUpdated(target, provenanceHash);
    }

    /// @notice Update metadata base URI and contract URI
    /// @param target target contract to update metadata for
    /// @param metadataBaseURI new base URI
    /// @param newContractURI new contract URI (can be an empty string)
    function updateMetadataBase(address target, string memory metadataBaseURI, string memory newContractURI) external requireSenderAdmin(target) {
        _updateMetadataDetails(target, metadataBaseURI, "", newContractURI, 0);
    }

    /// @notice Update metadata base URI, extension, contract URI and freezing details
    /// @param target target contract to update metadata for
    /// @param metadataBase new base URI to update metadata with
    /// @param metadataExtension new extension to append to base metadata URI
    /// @param freezeAt time to freeze the contract metadata at (set to 0 to disable)
    /// @param newContractURI new contract URI (can be an empty string)
    function updateMetadataBaseWithDetails(
        address target,
        string memory metadataBase,
        string memory metadataExtension,
        string memory newContractURI,
        uint256 freezeAt
    ) external requireSenderAdmin(target) {
        _updateMetadataDetails(target, metadataBase, metadataExtension, newContractURI, freezeAt);
    }

    /// @notice Internal metadata update function
    /// @param metadataBase Bbase URI to update metadata with
    /// @param metadataExtension extension URI to append to base metadata URI
    /// @param freezeAt timestamp to freeze metadata (set to 0 to disable freezing)
    function _updateMetadataDetails(
        address target,
        string memory metadataBase,
        string memory metadataExtension,
        string memory newContractURI,
        uint256 freezeAt
    ) internal {
        uint256 contractFreezeTime = metadataBaseByContract[target].freezeAt;
        if (contractFreezeTime != 0 && contractFreezeTime <= block.timestamp) {
            revert MetadataFrozen();
        }

        metadataBaseByContract[target] = MetadataURIInfo({base: metadataBase, extension: metadataExtension, contractURI: newContractURI, freezeAt: freezeAt});

        emit MetadataUpdated({
            target: target,
            metadataBase: metadataBase,
            metadataExtension: metadataExtension,
            contractURI: newContractURI,
            freezeAt: freezeAt
        });
    }

    function updateDynamicMetadataInfo(
        address target,
        string memory description,
        string memory imageURI,
        string memory animationURI
    ) external requireSenderAdmin(target) {
        _updateDynamicMetadataInfo(target, description, imageURI, animationURI);
    }

    function _updateDynamicMetadataInfo(
        address target,
        string memory description,
        string memory imageURI,
        string memory animationURI
    ) internal {
        dynamicMetadataInfoByContract[target] = DynamicMetadataInfo({
            description: description,
            imageURI: imageURI,
            animationURI: animationURI
        });

        emit DynamicMetadataUpdated({target: target, imageURI: imageURI, animationURI: animationURI});
    }

    /// @notice A contract URI for the given collection contract
    /// @dev reverts if a contract uri is not provided
    /// @return contract uri for the contract metadata
    function contractURI() external view override returns (string memory) {
        string memory uri = metadataBaseByContract[msg.sender].contractURI;
        if (bytes(uri).length == 0) revert();
        return uri;
    }

    /// @notice A token URI for the given collection contract
    /// @dev reverts if a contract uri is not set
    /// @return token URI for the given token ID and contract (set by msg.sender)
    function tokenURI(uint256 tokenId, bool revealed) external view override returns (string memory) {
        if (!revealed) {
            return dynamicTokenURI(tokenId);
        }
        MetadataURIInfo memory info = metadataBaseByContract[msg.sender];

        if (bytes(info.base).length == 0) revert();

        return string(abi.encodePacked(info.base, StringsUpgradeable.toString(tokenId), info.extension));
    }

    /// @notice A token URI for the given collection contract, handle when image is the same, ex, pre-reveal
    /// @dev reverts if a contract uri is not set
    /// @return token URI for the given token ID and contract (set by msg.sender)
    function dynamicTokenURI(uint256 tokenId) public view returns (string memory) {
        DynamicMetadataInfo memory info = dynamicMetadataInfoByContract[msg.sender];

        address target = msg.sender;

        if (bytes(info.imageURI).length == 0 && bytes(info.animationURI).length == 0) revert();

        return
            DynamicMetadataRenderer.createMetadata({
                name: IERC721MetadataUpgradeable(target).name(),
                description: info.description,
                imageURI: info.imageURI,
                animationURI: info.animationURI,
                isEdition: false,
                tokenId: tokenId
            });
    }
}

File 2 of 14 : StringsUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/MathUpgradeable.sol";

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = MathUpgradeable.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, MathUpgradeable.log256(value) + 1);
        }
    }

    /**
     * @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] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 3 of 14 : IERC721MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../token/ERC721/extensions/IERC721MetadataUpgradeable.sol";

File 4 of 14 : IMetadataRenderer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IMetadataRenderer {
    function tokenURI(uint256, bool) external view returns (string memory);
    function contractURI() external view returns (string memory);
    function initializeWithData(bytes memory metadataBase, bytes memory dynamicTokenData) external;
    function updateMetadataBase(
        address collection, 
        string memory baseURI, 
        string memory metadataURI
    ) external;
    function updateMetadataBaseWithDetails(
        address collection, 
        string memory baseURI, 
        string memory extension,
        string memory metadataURI,
        uint256 freezeAt
    ) external;
    function dynamicTokenURI(uint256) external view returns (string memory);
}

File 5 of 14 : DynamicMetadataRenderer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {Base64} from "@openzeppelin/contracts/utils/Base64.sol";

/// NFT metadata library for dynamically render metadata
library DynamicMetadataRenderer {
    /// Generate metadata from storage information as base64-json blob
    /// Combines the media data and metadata
    /// @param name Name of NFT in metadata
    /// @param description Description of NFT in metadata
    /// @param imageURI URI of image to render for edition
    /// @param animationURI URI of animation to render for edition
    /// @param isEdition collection type
    /// @param tokenId Token ID for specific token
    function createMetadata(
        string memory name,
        string memory description,
        string memory imageURI,
        string memory animationURI,
        bool isEdition,
        uint256 tokenId
    ) internal pure returns (string memory) {
        string memory _tokenMediaData = tokenMediaData(imageURI, animationURI);
        bytes memory json = createMetadataJSON(name, description, _tokenMediaData, isEdition, tokenId);
        return encodeMetadataJSON(json);
    }

    /// Function to create the metadata json string for the nft edition
    /// @param name Name of NFT in metadata
    /// @param description Description of NFT in metadata
    /// @param mediaData Data for media to include in json object
    /// @param isEdition different format for edition metadata
    /// @param tokenId Token ID for specific token
    function createMetadataJSON(
        string memory name,
        string memory description,
        string memory mediaData,
        bool isEdition,
        uint256 tokenId
    ) internal pure returns (bytes memory) {
        if (isEdition) {
            return
                abi.encodePacked(
                    '{"name": "',
                    name,
                    " #",
                    Strings.toString(tokenId),
                    '", "',
                    'description": "',
                    description,
                    '", "',
                    mediaData,
                    ','
                    '"properties": {"number": ',
                    Strings.toString(tokenId),
                    ', "name": "',
                    name,
                    '"}}'
                );
        } else {
            return abi.encodePacked('{"name": "', name, " #", Strings.toString(tokenId), '", "', 'description": "', description, '", "', mediaData, "}");
        }
    }

    function encodeContractURIJSON(
        string memory name,
        string memory description,
        string memory imageURI,
        string memory animationURI,
        uint256 royaltyBPS,
        address royaltyRecipient
    ) internal pure returns (string memory) {
        bytes memory imageSpace = bytes("");
        if (bytes(imageURI).length > 0) {
            imageSpace = abi.encodePacked('", "image": "', imageURI);
        }
        bytes memory animationSpace = bytes("");
        if (bytes(animationURI).length > 0) {
            animationSpace = abi.encodePacked('", "animation_url": "', animationURI);
        }

        return
            string(
                encodeMetadataJSON(
                    abi.encodePacked(
                        '{"name": "',
                        name,
                        '", "description": "',
                        description,
                        // this is for opensea since they don't respect ERC2981 right now
                        '", "seller_fee_basis_points": ',
                        Strings.toString(royaltyBPS),
                        ', "fee_recipient": "',
                        Strings.toHexString(uint256(uint160(royaltyRecipient)), 20),
                        imageSpace,
                        animationSpace,
                        '"}'
                    )
                )
            );
    }

    /// Encodes the argument json bytes into base64-data uri format
    /// @param json Raw json to base64 and turn into a data-uri
    function encodeMetadataJSON(bytes memory json) internal pure returns (string memory) {
        return string(abi.encodePacked("data:application/json;base64,", Base64.encode(json)));
    }

    /// Generates edition metadata from storage information as base64-json blob
    /// Combines the media data and metadata
    /// @param imageUrl URL of image to render for edition
    /// @param animationUrl URL of animation to render for edition
    function tokenMediaData(string memory imageUrl, string memory animationUrl) internal pure returns (string memory) {
        bool hasImage = bytes(imageUrl).length > 0;
        bool hasAnimation = bytes(animationUrl).length > 0;
        if (hasImage && hasAnimation) {
            return string(abi.encodePacked('image": "', imageUrl, '", "animation_url": "', animationUrl, '"'));
        }
        if (hasImage) {
            return string(abi.encodePacked('image": "', imageUrl, '"'));
        }
        if (hasAnimation) {
            return string(abi.encodePacked('animation_url": "', animationUrl, '"'));
        }

        return "";
    }
}

File 6 of 14 : MetadataRenderAdminCheck.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {IERC721Collection} from "../interfaces/IERC721Collection.sol";

contract MetadataRenderAdminCheck {
    error Access_OnlyAdmin();

    /// @notice Modifier to require the sender to be an admin
    /// @param target address that the user wants to modify
    modifier requireSenderAdmin(address target) {
        if (target != msg.sender && !IERC721Collection(target).isAdmin(msg.sender)) {
            revert Access_OnlyAdmin();
        }

        _;
    }
}

File 7 of 14 : MathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

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

pragma solidity ^0.8.0;

import "../IERC721Upgradeable.sol";

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

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

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

File 9 of 14 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @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] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 10 of 14 : Base64.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

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

            // Run over the input, 3 bytes at a time
            for {
                let dataPtr := data
                let endPtr := add(data, mload(data))
            } lt(dataPtr, endPtr) {

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

File 11 of 14 : IERC721Collection.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {IMetadataRenderer} from "../interfaces/IMetadataRenderer.sol";

/// @notice Interface for Freee Collection contract
interface IERC721Collection {
    // Enums
    /// @notice Phase type
    enum PhaseType {
        Public,
        Presale,
        Airdrop,
        AdminMint
    }

    // Access errors
    /// @notice Only admin can access this function
    error Access_OnlyAdmin();
    /// @notice Missing the given role or admin access
    error Access_MissingRoleOrAdmin(bytes32 role);
    /// @notice Withdraw is not allowed by this user
    error Access_WithdrawNotAllowed();
    /// @notice Cannot withdraw funds due to ETH send failure.
    error Withdraw_FundsSendFailure();
    /// @notice Call to external metadata renderer failed.
    error ExternalMetadataRenderer_CallFailed();

    // Sale/Purchase errors
    /// @notice Sale is inactive
    error Sale_Inactive();
    /// @notice Presale is inactive
    error Presale_Inactive();
    /// @notice Presale invalid, out of range
    error Presale_Invalid();
    /// @notice Exceed presale stage supply
    error Presale_ExceedStageSupply();
    /// @notice Presale merkle root is invalid
    error Presale_MerkleNotApproved();
    /// @notice Wrong price for purchase
    error Purchase_WrongPrice(uint256 correctPrice);
    /// @notice NFT sold out
    error Mint_SoldOut();
    /// @notice Too many purchase for address
    error Purchase_TooManyForAddress();
    /// @notice Too many presale for address
    error Presale_TooManyForAddress();
    /// @notice Collection already revealed
    error Collection_Aready_Revealed();
    /// @notice Trading locked before mint out
    error Collection_TradingLocked();

    // Admin errors
    /// @notice Presale stage out of supported range
    error Setup_Presale_StageOutOfRange();
    /// @notice Royalty percentage too high
    error Setup_RoyaltyPercentageTooHigh(uint16 maxRoyaltyBPS);
    /// @notice invalid collection size when update
    error Admin_InvalidCollectionSize();

    /// @notice Event emitted for mint fee payout
    /// @param mintFeeAmount amount of the mint fee
    /// @param mintFeeRecipient recipient of the mint fee
    /// @param success if the payout succeeded
    event MintFeePayout(uint256 mintFeeAmount, address mintFeeRecipient, bool success);

    /// @notice Event emitted for each sale
    /// @param phase phase of the sale
    /// @param to address sale was made to
    /// @param quantity quantity of the minted nfts
    /// @param pricePerToken price for each token
    /// @param firstPurchasedTokenId first purchased token ID (to get range add to quantity for max)
    /// @param presaleStage stageIndex of presale stage if applicable, else return 0
    event Sale(PhaseType phase, address indexed to, uint256 indexed quantity, uint256 indexed pricePerToken, uint256 firstPurchasedTokenId, uint256 presaleStage);

    /// @notice Event emitted for each sale
    /// @param sender address sale was made to
    /// @param tokenContract address of the token contract
    /// @param tokenId first purchased token ID (to get range add to quantity for max)
    /// @param quantity quantity of the minted nfts
    /// @param comment caller provided comment
    event MintComment(address indexed sender, address indexed tokenContract, uint256 indexed tokenId, uint256 quantity, string comment);

    /// @notice Contract has been configured and published
    /// @param changedBy Changed by user
    event ContractStatusChanged(address indexed changedBy);

    /// @notice Sales configuration has been changed
    /// @dev To access new sales configuration, use getter function.
    /// @param changedBy Changed by user
    event PublicSaleConfigChanged(address indexed changedBy);

    /// @notice Presale config changed
    /// @param changedBy changed by user
    event PresaleConfigChanged(address indexed changedBy);

    /// @notice Collection size reduced
    /// @param changedBy changed by user
    /// @param newSize new collection size
    event CollectionSizeReduced(address indexed changedBy, uint64 newSize);

    /// @notice event emit when user change the lock trading func
    /// @param changedBy changed by user
    /// @param status new status
    event LockTradingStatusChanged(address indexed changedBy, bool status);

    /// @notice Event emitted when the royalty percentage changed
    /// @param changedBy address that change the royalty
    /// @param newPercentage new royalty percentage
    /// @param newRecipient new royalty recipient
    event RoyaltyChanged(address indexed changedBy, uint256 newPercentage, address newRecipient);

    /// @notice Event emitted when the funds recipient is changed
    /// @param newAddress new address for the funds recipient
    /// @param changedBy address that the recipient is changed by
    event FundsRecipientChanged(address indexed newAddress, address indexed changedBy);

    /// @notice Event emitted when the funds are withdrawn from the minting contract
    /// @param withdrawnBy address that issued the withdraw
    /// @param withdrawnTo address that the funds were withdrawn to
    /// @param amount amount that was withdrawn
    /// @param feeRecipient user getting withdraw fee (if any)
    /// @param feeAmount amount of the fee getting sent (if any)
    event FundsWithdrawn(address indexed withdrawnBy, address indexed withdrawnTo, uint256 amount, address feeRecipient, uint256 feeAmount);

    /// @notice Collection dynamic metadata changed
    /// @param changedBy address that changed the info
    event DynamicMetadataChanged(address changedBy);

    /// @notice Collection has been revealed
    /// @param revealedBy Revealed by user
    event CollectionRevealed(address indexed revealedBy);

    /// @notice Event emitted when metadata renderer is updated.
    /// @param sender address of the updater
    /// @param renderer new metadata renderer address
    event UpdatedMetadataRenderer(address sender, IMetadataRenderer renderer);

    /// @notice General configuration for NFT Minting and bookkeeping
    struct Configuration {
        /// @dev Metadata renderer
        IMetadataRenderer metadataRenderer;
        /// @dev Max supply of collection
        uint64 collectionSize;
        /// @dev Royalty amount in bps
        uint16 royaltyBPS;
        /// @dev Funds recipient for sale
        address payable fundsRecipient;
        /// @dev Royalty recipient for secondary sale
        address payable royaltyRecipient;
        /// @dev collection reveal status
        bool revealed;
        /// @dev lock trading before mint out
        bool lockBeforeMintOut;
    }

    /// @notice Public sale configuration
    /// @dev Uses 1 storage slot
    struct PublicSaleConfiguration {
        /// @dev Public sale price (max ether value > 1000 ether with this value)
        uint104 publicSalePrice;
        /// @dev Purchase mint limit per address (if set to 0 === unlimited mints)
        uint32 maxSalePurchasePerAddress;
        /// @dev uint64 type allows for dates into 292 billion years
        uint64 publicSaleStart;
        uint64 publicSaleEnd;
        /// @dev Whether public sale is disabled
        bool publicSaleDisabled;
    }

    /// @notice Presale stage configuration
    struct PresaleConfiguration {
        /// @notice Presale stage human readable name
        string presaleName;
        /// @notice Presale start timestamp
        uint64 presaleStart;
        /// @notice Presale end timestamp
        uint64 presaleEnd;
        /// @notice Presale price in ether
        uint104 presalePrice;
        /// @notice Purchase mint limit per address (if set to 0 === unlimited mints)
        uint32 presaleMaxPurchasePerAddress;
        /// @notice supply allocated for presale stage
        uint32 presaleSupply;
        /// @notice amount minted for presale stage
        uint32 presaleMinted;
        /// @notice Presale merkle root
        bytes32 presaleMerkleRoot;
    }

    /// @notice Return type of specific mint counts and details per address
    struct AddressMintDetails {
        /// Number of presale mints for each stage from the given address
        uint256[] presaleMintsByStage;
        /// Number of presale mints from the given address
        uint256 presaleMints;
        /// Number of public mints from the given address
        uint256 publicMints;
        /// Number of total mints from the given address
        uint256 totalMints;
    }

    /// @notice External purchase function (payable in eth)
    /// @param quantity to purchase
    /// @return first minted token ID
    function purchase(uint256 quantity) external payable returns (uint256);

    /// @notice External purchase presale function (takes a merkle proof and matches to root) (payable in eth)
    /// @param stageIndex targetted presale stage
    /// @param quantity to purchase
    /// @param maxQuantity can purchase (verified by merkle root)
    /// @param pricePerToken price per token allowed (verified by merkle root)
    /// @param merkleProof input for merkle proof leaf verified by merkle root
    /// @return first minted token ID
    function purchasePresale(uint256 stageIndex, uint256 quantity, uint256 maxQuantity, uint256 pricePerToken, bytes32[] memory merkleProof) external payable returns (uint256);

    /// @notice Function to return the specific sales details for a given address
    /// @param minter address for minter to return mint information for
    function mintedPerAddress(address minter) external view returns (AddressMintDetails memory);

    /// @notice This is the opensea/public owner setting that can be set by the contract admin
    function owner() external view returns (address);

    /// @notice Admin function to update the public sale configuration settings
    /// @param newConfig updated public stage config
    function setPublicSaleConfiguration(PublicSaleConfiguration memory newConfig) external;

    /// @notice Admin function to update the presale configuration settings
    /// @param newConfig new presale configuration
    function setPresaleConfiguration(PresaleConfiguration[] calldata newConfig) external;

    /// @notice Admin function to reduce collection size (cut suppy)
    /// @param _newCollectionSize new collection size
    function reduceSupply(uint64 _newCollectionSize) external;

    /// @dev Reveal collection artworks
    /// @param collectionURI collection artwork URI
    /// @param extension collection artwork URI extension
    function revealCollection(string memory collectionURI, string memory extension) external;

    /// @notice Update the metadata renderer
    /// @param newRenderer new address for renderer
    /// @param metadataBase data to call to bootstrap data for the new renderer (optional)
    /// @param dynamicMetadataInfo data to call to bootstrap dynamic metadata for the new renderer (optional)
    function setMetadataRenderer(address newRenderer, bytes memory metadataBase, bytes memory dynamicMetadataInfo) external;

    /// @notice This is an admin mint function to mint a quantity to a specific address
    /// @param to address to mint to
    /// @param quantity quantity to mint
    /// @return the id of the first minted NFT
    function adminMint(address to, uint256 quantity) external returns (uint256);

    /// @notice This is an admin mint function to mint a single nft each to a list of addresses
    /// @param to list of addresses to mint an NFT each to
    /// @return the id of the first minted NFT
    function adminMintAirdrop(address[] memory to) external returns (uint256);

    /// @dev Getter for admin role associated with the contract to handle metadata
    /// @return boolean if address is admin
    function isAdmin(address user) external view returns (bool);
}

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

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165Upgradeable.sol";

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

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

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

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

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

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

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

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

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

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

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

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

File 13 of 14 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165Upgradeable {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Settings
{
  "remappings": [
    "solady/=lib/solady/",
    "solemate/=/lib/solemate/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "forge-std/=lib/forge-std/src/",
    "erc721a/contracts/=lib/ERC721A/contracts/",
    "erc721a-upgradeable/contracts/=lib/ERC721A-Upgradeable/contracts/",
    "@limitbreak/creator-token-standards/src/=lib/creator-token-standards/src/",
    "@limitbreak/permit-c/=lib/creator-token-standards/lib/PermitC/src/",
    "@opensea/tstorish/=lib/creator-token-standards/lib/tstorish/src/",
    "@openzeppelin/=lib/creator-token-standards/lib/openzeppelin-contracts/",
    "@rari-capital/solmate/=lib/creator-token-standards/lib/PermitC/lib/solmate/",
    "ERC721A-Upgradeable/=lib/ERC721A-Upgradeable/contracts/",
    "ERC721A/=lib/creator-token-standards/lib/ERC721A/contracts/",
    "PermitC/=lib/creator-token-standards/lib/PermitC/",
    "creator-token-standards/=lib/creator-token-standards/",
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "erc4626-tests/=lib/creator-token-standards/lib/PermitC/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "erc721a/=lib/creator-token-standards/lib/ERC721A/",
    "forge-gas-metering/=lib/creator-token-standards/lib/PermitC/lib/forge-gas-metering/",
    "murky/=lib/creator-token-standards/lib/murky/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/creator-token-standards/lib/PermitC/lib/openzeppelin-contracts/contracts/",
    "solmate/=lib/solmate/src/",
    "tstorish/=lib/creator-token-standards/lib/tstorish/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"Access_OnlyAdmin","type":"error"},{"inputs":[],"name":"MetadataFrozen","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"string","name":"imageURI","type":"string"},{"indexed":false,"internalType":"string","name":"animationURI","type":"string"}],"name":"DynamicMetadataUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"string","name":"metadataBase","type":"string"},{"indexed":false,"internalType":"string","name":"metadataExtension","type":"string"},{"indexed":false,"internalType":"string","name":"contractURI","type":"string"},{"indexed":false,"internalType":"uint256","name":"freezeAt","type":"uint256"}],"name":"MetadataUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bytes32","name":"provenanceHash","type":"bytes32"}],"name":"ProvenanceHashUpdated","type":"event"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"dynamicMetadataInfoByContract","outputs":[{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"imageURI","type":"string"},{"internalType":"string","name":"animationURI","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"dynamicTokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"metadataBase","type":"bytes"},{"internalType":"bytes","name":"dynamicTokenData","type":"bytes"}],"name":"initializeWithData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"metadataBaseByContract","outputs":[{"internalType":"string","name":"base","type":"string"},{"internalType":"string","name":"extension","type":"string"},{"internalType":"string","name":"contractURI","type":"string"},{"internalType":"uint256","name":"freezeAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"provenanceHashes","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bool","name":"revealed","type":"bool"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"imageURI","type":"string"},{"internalType":"string","name":"animationURI","type":"string"}],"name":"updateDynamicMetadataInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"string","name":"metadataBaseURI","type":"string"},{"internalType":"string","name":"newContractURI","type":"string"}],"name":"updateMetadataBase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"string","name":"metadataBase","type":"string"},{"internalType":"string","name":"metadataExtension","type":"string"},{"internalType":"string","name":"newContractURI","type":"string"},{"internalType":"uint256","name":"freezeAt","type":"uint256"}],"name":"updateMetadataBaseWithDetails","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes32","name":"provenanceHash","type":"bytes32"}],"name":"updateProvenanceHash","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60808060405234601557612077908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80630bafbe0814611198578063301a32601461116c57806342495a9514610cf45780636bbde001146107b757806391074f9814610754578063af806d56146102be578063cc1306db146101d3578063d644beac1461014a578063de5af49b14610112578063e8a3d485146100cd5763ea58a14d14610092575f80fd5b346100c95760203660031901126100c9576100c56100b160043561163b565b604051918291602083526020830190611366565b0390f35b5f80fd5b346100c9575f3660031901126100c957335f525f6020526100f3600260405f20016113c2565b8051156100c9576100c590604051918291602083526020830190611366565b346100c95760203660031901126100c9576001600160a01b0361013361127d565b165f526002602052602060405f2054604051908152f35b346100c95760203660031901126100c9576001600160a01b0361016b61127d565b165f5260016020526101b760405f206100c5610186826113c2565b916101c56101a2600261019b600185016113c2565b93016113c2565b91604051958695606087526060870190611366565b908582036020870152611366565b908382036040850152611366565b346100c95760403660031901126100c9576101ec61127d565b6001600160a01b03166024353382141580610252575b6102405760207ff2e078c4022bfd6c56addd06540a4a5dd4252b6b2c424b6840c184063f48fc2791835f52600282528060405f2055604051908152a2005b6040516302bd6bd160e01b8152600490fd5b50604051630935e01b60e21b8152336004820152602081602481865afa9081156102b3575f91610284575b5015610202565b6102a6915060203d6020116102ac575b61029e81836112e4565b8101906114c2565b8361027d565b503d610294565b6040513d5f823e3d90fd5b346100c95760603660031901126100c9576102d761127d565b6001600160401b036024358181116100c9576102f7903690600401611320565b906044358181116100c957610310903690600401611320565b926001600160a01b0316913383141580610703575b6102405760405191610336836112c9565b5f8352835f5260205f81526003908160405f20015480151590816106f8575b506106e6576040519261036784611293565b84845281840190868252604085019189835260608601935f8552895f525f815260405f20965190815184811161056257806103a28a5461138a565b93601f9485811161069a575b508390858311600114610638575f9261062d575b50508160011b915f19908a1b1c19161788555b60019283890190518051908682116105625781906103f3845461138a565b8681116105df575b508490868311600114610581575f92610576575b50505f19828b1b1c191690851b1790555b6002880194519081519485116105625761043a865461138a565b83811161051f575b50809285116001146104a3575092805f805160206120228339815191529b9c97959381936104939a98965f94610498575b50501b915f1990861b1c19161790555b5191015560405193849384611464565b0390a2005b015192508e80610473565b9190601f949394198416865f52835f20935f905b828210610508575050916104939997959391855f805160206120228339815191529e9f9a989694106104f1575b505050811b019055610483565b01515f1983881b60f8161c191690558c80806104e4565b8088869782949787015181550196019401906104b7565b865f52815f208480880160051c820192848910610559575b0160051c019085905b82811061054e575050610442565b5f8155018590610540565b92508192610537565b634e487b7160e01b5f52604160045260245ffd5b015190508f8061040f565b90879350601f19831691855f52865f20925f5b888282106105c957505084116105b2575b505050811b019055610420565b01515f19838d1b60f8161c191690558f80806105a5565b8385015186558b97909501949384019301610594565b909150835f52845f208680850160051c820192878610610624575b918991869594930160051c01915b8281106106165750506103fb565b5f8155859450899101610608565b925081926105fa565b015190508e806103c2565b5f8c81528581209350601f198516905b8682821061068457505090846001959493921061066d575b505050811b0188556103d5565b01515f19838c1b60f8161c191690558e8080610660565b6001859682939686015181550195019301610648565b9091508a5f52835f208580850160051c8201928686106106dd575b9085949392910160051c01905b8181106106cf57506103ae565b5f81558493506001016106c2565b925081926106b5565b60405163777821ff60e11b8152600490fd5b905042101588610355565b50604051630935e01b60e21b8152336004820152602081602481875afa9081156102b3575f91610735575b5015610325565b61074e915060203d6020116102ac5761029e81836112e4565b8561072e565b346100c95760203660031901126100c9576001600160a01b0361077561127d565b165f525f60205260405f20610789816113c2565b6100c5610798600184016113c2565b9260036107a7600283016113c2565b91015490604051948594856114a4565b346100c95760403660031901126100c9576004356001600160401b0381116100c9576107e7903690600401611320565b6024356001600160401b0381116100c957610806903690600401611320565b81518061089e575b50809150518061081a57005b8101606082602083019203126100c95760208201516001600160401b0381116100c95781602061084c928501016115f5565b9060408301516001600160401b0381116100c95781602061086f928601016115f5565b906060840151906001600160401b0382116100c95760206108959261089c9601016115f5565b913361188f565b005b820191604081602085019403126100c95760208101516001600160401b0381116100c9578360206108d1928401016115f5565b926040820151916001600160401b0383116100c9576108f392016020016115f5565b9160405192610901846112c9565b5f8452335f525f602052600360405f2001548015159081610ce9575b506106e6576040519361092f85611293565b8285528060208601528160408601525f6060860152335f525f60205260405f2085518051906001600160401b03821161056257819061096e845461138a565b601f8111610c99575b50602090601f8311600114610c31575f92610c26575b50508160011b915f199060031b1c19161781555b60208601518051906001600160401b0382116105625781906109c6600185015461138a565b601f8111610bd3575b50602090601f8311600114610b65575f92610b5a575b50508160011b915f199060031b1c19161760018201555b60408601519586516001600160401b03811161056257610a1f600284015461138a565b601f8111610b16575b506020601f8211600114610a975791816060926003945f80516020612022833981519152999a9b5f92610a8c575b50508160011b915f1990861b1c19161760028501555b0151910155610a82604051928392339684611464565b0390a2808261080e565b015190508b80610a56565b600284015f5260205f20985f5b601f1984168110610afe5750825f8051602061202283398151915298999a60039593600193606096601f19811610610ae7575b505050811b016002850155610a6c565b01515f1983881b60f8161c191690558b8080610ad7565b828201518b556001909a019960209283019201610aa4565b600284015f5260205f20601f830160051c810160208410610b53575b601f830160051c82018110610b48575050610a28565b5f8155600101610b32565b5080610b32565b0151905088806109e5565b9250600184015f5260205f20905f935b601f1984168510610bb8576001945083601f19811610610ba0575b505050811b0160018201556109fc565b01515f1960f88460031b161c19169055888080610b90565b81810151835560209485019460019093019290910190610b75565b909150600184015f5260205f20601f840160051c81019160208510610c1c575b90601f859493920160051c01905b818110610c0e57506109cf565b5f8155849350600101610c01565b9091508190610bf3565b01519050888061098d565b9250835f5260205f20905f935b601f1984168510610c7e576001945083601f19811610610c66575b505050811b0181556109a1565b01515f1960f88460031b161c19169055888080610c59565b81810151835560209485019460019093019290910190610c3e565b909150835f5260205f20601f840160051c81019160208510610cdf575b90601f859493920160051c01905b818110610cd15750610977565b5f8155849350600101610cc4565b9091508190610cb6565b90504210158561091d565b346100c95760a03660031901126100c957610d0d61127d565b6001600160401b036024358181116100c957610d2d903690600401611320565b6044358281116100c957610d45903690600401611320565b906064358381116100c957610d5e903690600401611320565b608435946001600160a01b031693338514158061111b575b61024057845f5260205f81526003908160405f2001548015159081611110575b506106e65760405192610da884611293565b85845281840190878252604085019186835260608601938b85528a5f525f815260405f2096519081518481116105625780610de38a5461138a565b93601f948581116110c4575b508390858311600114611062575f92611057575b50508160011b915f19908a1b1c19161788555b6001928389019051805190868211610562578190610e34845461138a565b868111611009575b508490868311600114610fab575f92610fa0575b50505f19828b1b1c191690851b1790555b60028801945190815194851161056257610e7b865461138a565b838111610f5d575b5080928511600114610ee0575092806104939997959381935f805160206120228339815191529e9f9a98965f94610ed5575b50501b915f1990861b1c19161790555b51910155604051948594856114a4565b015192508f80610eb5565b9190601f949394198416865f52835f20935f905b828210610f46575050915f805160206120228339815191529d9e9997959391856104939c9a98969410610f2f575b505050811b019055610ec5565b01515f1983881b60f8161c191690558d8080610f22565b808886978294978701518155019601940190610ef4565b865f52815f208480880160051c820192848910610f97575b0160051c019085905b828110610f8c575050610e83565b5f8155018590610f7e565b92508192610f75565b015190505f80610e50565b90879350601f19831691855f52865f20925f5b88828210610ff35750508411610fdc575b505050811b019055610e61565b01515f19838d1b60f8161c191690555f8080610fcf565b8385015186558b97909501949384019301610fbe565b909150835f52845f208680850160051c82019287861061104e575b918991869594930160051c01915b828110611040575050610e3c565b5f8155859450899101611032565b92508192611024565b015190508f80610e03565b5f8c81528581209350601f198516905b868282106110ae575050908460019594939210611097575b505050811b018855610e16565b01515f19838c1b60f8161c191690558f808061108a565b6001859682939686015181550195019301611072565b9091508a5f52835f208580850160051c820192868610611107575b9085949392910160051c01905b8181106110f95750610def565b5f81558493506001016110ec565b925081926110df565b905042101589610d96565b50604051630935e01b60e21b8152336004820152602081602481895afa9081156102b3575f9161114d575b5015610d76565b611166915060203d6020116102ac5761029e81836112e4565b87611146565b346100c95760403660031901126100c95760243580151581036100c9576100b16100c5916004356114da565b346100c95760803660031901126100c9576111b161127d565b6001600160401b036024358181116100c9576111d1903690600401611320565b906044358181116100c9576111ea903690600401611320565b906064359081116100c957611203903690600401611320565b916001600160a01b038416338114159081611227575b506102405761089c9361188f565b604051630935e01b60e21b81523360048201529150602090829060249082905afa9081156102b3575f9161125e575b501585611219565b611277915060203d6020116102ac5761029e81836112e4565b85611256565b600435906001600160a01b03821682036100c957565b608081019081106001600160401b0382111761056257604052565b606081019081106001600160401b0382111761056257604052565b602081019081106001600160401b0382111761056257604052565b90601f801991011681019081106001600160401b0382111761056257604052565b6001600160401b03811161056257601f01601f191660200190565b81601f820112156100c95780359061133782611305565b9261134560405194856112e4565b828452602083830101116100c957815f926020809301838601378301015290565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90600182811c921680156113b8575b60208310146113a457565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611399565b9060405191825f82546113d48161138a565b908184526020946001916001811690815f146114425750600114611404575b505050611402925003836112e4565b565b5f90815285812095935091905b81831061142a57505061140293508201015f80806113f3565b85548884018501529485019487945091830191611411565b9250505061140294925060ff191682840152151560051b8201015f80806113f3565b939261149f906114915f9461148360609560808a5260808a0190611366565b9088820360208a0152611366565b908682036040880152611366565b930152565b94939261149160609361148361149f9460808a5260808a0190611366565b908160209103126100c9575180151581036100c95790565b90156115ec57335f526020905f825260405f20906040516114fa81611293565b611503836113c2565b81526001906003611516600186016113c2565b94868301958652611529600282016113c2565b60408401520154606082015251908151156100c95791849261154a82611cab565b9181602161155a60018601611c79565b94850101905b6115b6575b5050508290816115b39551916040519785899651918291018488015e8501908282015f8152815193849201905e01908282015f8152815193849201905e015f838201520380845201826112e4565b90565b5f190190600a906f181899199a1a9b1b9c1cb0b131b232b360811b8282061a8353049182156115e757919082611560565b611565565b6115b39061163b565b81601f820112156100c95780519061160c82611305565b9261161a60405194856112e4565b828452602083830101116100c957815f9260208093018386015e8301015290565b335f5260019060209160018352604090815f209282519161165b836112ae565b611664856113c2565b83526116846002611677600188016113c2565b96888601978852016113c2565b84840190808252865151159081611885575b506100c95784516306fdde0360e01b8152935f85600481335afa94851561187b57908895949392915f95611832575b5051965190519192916116d791611d85565b916116e182611cab565b918160216116f160018601611c79565b94850101905b6117fc575b50505060446117b393856115b3988195828a996117ae97603d9c51998a97693d913730b6b2911d101160b11b828a0152805191829101602a8a015e87019061202360f01b602a830152805192839101602c83015e01631116101160e11b9384602c8301526e3232b9b1b934b83a34b7b7111d101160891b6030830152805192839101603f83015e0191603f830152805192839101604383015e01607d60f81b60438201520360248101845201826112e4565b611ec5565b90519384917f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000828401528051918291018484015e81015f8382015203601d8101845201826112e4565b5f190190600a906f181899199a1a9b1b9c1cb0b131b232b360811b8282061a83530491821561182d579190826116f7565b6116fc565b91955093503d805f833e61184681836112e4565b81019488828703126100c9578151916001600160401b0383116100c95789966116d79361187392016115f5565b9490916116c5565b86513d5f823e3d90fd5b905051155f611696565b906040519061189d826112ae565b81526020810183815284604083015260018060a01b0383165f52600160205260405f209082518051906001600160401b0382116105625781906118e0855461138a565b601f8111611c29575b50602090601f8311600114611bc5575f92611bba575b50508160011b915f199060031b1c19161782555b51805160018301916001600160401b03821161056257611933835461138a565b601f8111611b75575b50602090601f8311600114611b0b57918060029492604096945f92611b00575b50508160011b915f199060031b1c19161790555b019101518051906001600160401b0382116105625761198f835461138a565b601f8111611abb575b50602090601f8311600114611a2d579282611a1d93611a0496937faeb55777aebd643816b537b1251e9b2d6ec76dacb36ac8991ec89c41953fd7e098965f92611a22575b50508160011b915f199060031b1c19161790555b604051938493604085526040850190611366565b83810360208501526001600160a01b0390911695611366565b0390a2565b015190505f806119dc565b90601f19831691845f5260205f20925f5b818110611aa3575093611a0496937faeb55777aebd643816b537b1251e9b2d6ec76dacb36ac8991ec89c41953fd7e098969360019383611a1d9810611a8b575b505050811b0190556119f0565b01515f1960f88460031b161c191690555f8080611a7e565b92936020600181928786015181550195019301611a3e565b835f5260205f20601f840160051c81019160208510611af6575b601f0160051c01905b818110611aeb5750611998565b5f8155600101611ade565b9091508190611ad5565b015190505f8061195c565b90601f19831691845f5260205f20925f5b818110611b5d5750926001928592604098966002989610611b45575b505050811b019055611970565b01515f1960f88460031b161c191690555f8080611b38565b92936020600181928786015181550195019301611b1c565b835f5260205f20601f840160051c81019160208510611bb0575b601f0160051c01905b818110611ba5575061193c565b5f8155600101611b98565b9091508190611b8f565b015190505f806118ff565b5f868152602081209350601f198516905b818110611c115750908460019594939210611bf9575b505050811b018255611913565b01515f1960f88460031b161c191690555f8080611bec565b92936020600181928786015181550195019301611bd6565b909150845f5260205f20601f840160051c81019160208510611c6f575b90601f859493920160051c01905b818110611c6157506118e9565b5f8155849350600101611c54565b9091508190611c46565b90611c8382611305565b611c9060405191826112e4565b8281528092611ca1601f1991611305565b0190602036910137565b5f907a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000080821015611d78575b50600a906d04ee2d6d415b85acef810000000080821015611d6b575b50662386f26fc1000080821015611d5e575b506305f5e10080821015611d51575b5061271080821015611d44575b506064811015611d36575b1015611d305790565b60010190565b606460029104920191611d27565b600491049201915f611d1c565b600891049201915f611d0f565b601091049201915f611d00565b602091049201915f611cee565b604092509004600a611cd2565b805115159082511515918080611ebe575b611e4a57611e025750611db65750604051611db0816112c9565b5f815290565b6115b360326020926040519384917030b734b6b0ba34b7b72fbab936111d101160791b82840152805191829101603184015e8101601160f91b60318201520360128101845201826112e4565b602092506115b39150602a906040519384916834b6b0b3b2911d101160b91b82840152805191829101602984015e8101601160f91b602982015203600a8101845201826112e4565b506115b39150603f90602080946040519586936834b6b0b3b2911d101160b91b82860152805191829101602986015e830190741116101130b734b6b0ba34b7b72fbab936111d101160591b6029830152805192839101603e83015e01601160f91b603e82015203601f8101845201826112e4565b5082611d96565b80511561201457604051611ed8816112ae565b604081527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208201527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f604082015281519160029260028101809111612000576003908190046001600160fe1b038116810361200057611f5f906002959492951b611c79565b936020850193839284518501935b848110611fad575050505050600390510680600114611f9b57600214611f91575090565b603d905f19015390565b50603d90815f19820153600119015390565b8360049197929394959701918251600190603f9082828260121c16880101518453828282600c1c16880101518385015382828260061c168801015188850153168501015186820153019593929190611f6d565b634e487b7160e01b5f52601160045260245ffd5b50604051611db0816112c956fe5eff125d5659803f33dbda215d6e8bfe0a404fd213a9ecb5e61973ee16cb17e7a2646970667358221220a44770d6a69e8235dcbfc6961c4e1d883363aaed265dfd299161f3504a7c3e0f64736f6c63430008190033

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c80630bafbe0814611198578063301a32601461116c57806342495a9514610cf45780636bbde001146107b757806391074f9814610754578063af806d56146102be578063cc1306db146101d3578063d644beac1461014a578063de5af49b14610112578063e8a3d485146100cd5763ea58a14d14610092575f80fd5b346100c95760203660031901126100c9576100c56100b160043561163b565b604051918291602083526020830190611366565b0390f35b5f80fd5b346100c9575f3660031901126100c957335f525f6020526100f3600260405f20016113c2565b8051156100c9576100c590604051918291602083526020830190611366565b346100c95760203660031901126100c9576001600160a01b0361013361127d565b165f526002602052602060405f2054604051908152f35b346100c95760203660031901126100c9576001600160a01b0361016b61127d565b165f5260016020526101b760405f206100c5610186826113c2565b916101c56101a2600261019b600185016113c2565b93016113c2565b91604051958695606087526060870190611366565b908582036020870152611366565b908382036040850152611366565b346100c95760403660031901126100c9576101ec61127d565b6001600160a01b03166024353382141580610252575b6102405760207ff2e078c4022bfd6c56addd06540a4a5dd4252b6b2c424b6840c184063f48fc2791835f52600282528060405f2055604051908152a2005b6040516302bd6bd160e01b8152600490fd5b50604051630935e01b60e21b8152336004820152602081602481865afa9081156102b3575f91610284575b5015610202565b6102a6915060203d6020116102ac575b61029e81836112e4565b8101906114c2565b8361027d565b503d610294565b6040513d5f823e3d90fd5b346100c95760603660031901126100c9576102d761127d565b6001600160401b036024358181116100c9576102f7903690600401611320565b906044358181116100c957610310903690600401611320565b926001600160a01b0316913383141580610703575b6102405760405191610336836112c9565b5f8352835f5260205f81526003908160405f20015480151590816106f8575b506106e6576040519261036784611293565b84845281840190868252604085019189835260608601935f8552895f525f815260405f20965190815184811161056257806103a28a5461138a565b93601f9485811161069a575b508390858311600114610638575f9261062d575b50508160011b915f19908a1b1c19161788555b60019283890190518051908682116105625781906103f3845461138a565b8681116105df575b508490868311600114610581575f92610576575b50505f19828b1b1c191690851b1790555b6002880194519081519485116105625761043a865461138a565b83811161051f575b50809285116001146104a3575092805f805160206120228339815191529b9c97959381936104939a98965f94610498575b50501b915f1990861b1c19161790555b5191015560405193849384611464565b0390a2005b015192508e80610473565b9190601f949394198416865f52835f20935f905b828210610508575050916104939997959391855f805160206120228339815191529e9f9a989694106104f1575b505050811b019055610483565b01515f1983881b60f8161c191690558c80806104e4565b8088869782949787015181550196019401906104b7565b865f52815f208480880160051c820192848910610559575b0160051c019085905b82811061054e575050610442565b5f8155018590610540565b92508192610537565b634e487b7160e01b5f52604160045260245ffd5b015190508f8061040f565b90879350601f19831691855f52865f20925f5b888282106105c957505084116105b2575b505050811b019055610420565b01515f19838d1b60f8161c191690558f80806105a5565b8385015186558b97909501949384019301610594565b909150835f52845f208680850160051c820192878610610624575b918991869594930160051c01915b8281106106165750506103fb565b5f8155859450899101610608565b925081926105fa565b015190508e806103c2565b5f8c81528581209350601f198516905b8682821061068457505090846001959493921061066d575b505050811b0188556103d5565b01515f19838c1b60f8161c191690558e8080610660565b6001859682939686015181550195019301610648565b9091508a5f52835f208580850160051c8201928686106106dd575b9085949392910160051c01905b8181106106cf57506103ae565b5f81558493506001016106c2565b925081926106b5565b60405163777821ff60e11b8152600490fd5b905042101588610355565b50604051630935e01b60e21b8152336004820152602081602481875afa9081156102b3575f91610735575b5015610325565b61074e915060203d6020116102ac5761029e81836112e4565b8561072e565b346100c95760203660031901126100c9576001600160a01b0361077561127d565b165f525f60205260405f20610789816113c2565b6100c5610798600184016113c2565b9260036107a7600283016113c2565b91015490604051948594856114a4565b346100c95760403660031901126100c9576004356001600160401b0381116100c9576107e7903690600401611320565b6024356001600160401b0381116100c957610806903690600401611320565b81518061089e575b50809150518061081a57005b8101606082602083019203126100c95760208201516001600160401b0381116100c95781602061084c928501016115f5565b9060408301516001600160401b0381116100c95781602061086f928601016115f5565b906060840151906001600160401b0382116100c95760206108959261089c9601016115f5565b913361188f565b005b820191604081602085019403126100c95760208101516001600160401b0381116100c9578360206108d1928401016115f5565b926040820151916001600160401b0383116100c9576108f392016020016115f5565b9160405192610901846112c9565b5f8452335f525f602052600360405f2001548015159081610ce9575b506106e6576040519361092f85611293565b8285528060208601528160408601525f6060860152335f525f60205260405f2085518051906001600160401b03821161056257819061096e845461138a565b601f8111610c99575b50602090601f8311600114610c31575f92610c26575b50508160011b915f199060031b1c19161781555b60208601518051906001600160401b0382116105625781906109c6600185015461138a565b601f8111610bd3575b50602090601f8311600114610b65575f92610b5a575b50508160011b915f199060031b1c19161760018201555b60408601519586516001600160401b03811161056257610a1f600284015461138a565b601f8111610b16575b506020601f8211600114610a975791816060926003945f80516020612022833981519152999a9b5f92610a8c575b50508160011b915f1990861b1c19161760028501555b0151910155610a82604051928392339684611464565b0390a2808261080e565b015190508b80610a56565b600284015f5260205f20985f5b601f1984168110610afe5750825f8051602061202283398151915298999a60039593600193606096601f19811610610ae7575b505050811b016002850155610a6c565b01515f1983881b60f8161c191690558b8080610ad7565b828201518b556001909a019960209283019201610aa4565b600284015f5260205f20601f830160051c810160208410610b53575b601f830160051c82018110610b48575050610a28565b5f8155600101610b32565b5080610b32565b0151905088806109e5565b9250600184015f5260205f20905f935b601f1984168510610bb8576001945083601f19811610610ba0575b505050811b0160018201556109fc565b01515f1960f88460031b161c19169055888080610b90565b81810151835560209485019460019093019290910190610b75565b909150600184015f5260205f20601f840160051c81019160208510610c1c575b90601f859493920160051c01905b818110610c0e57506109cf565b5f8155849350600101610c01565b9091508190610bf3565b01519050888061098d565b9250835f5260205f20905f935b601f1984168510610c7e576001945083601f19811610610c66575b505050811b0181556109a1565b01515f1960f88460031b161c19169055888080610c59565b81810151835560209485019460019093019290910190610c3e565b909150835f5260205f20601f840160051c81019160208510610cdf575b90601f859493920160051c01905b818110610cd15750610977565b5f8155849350600101610cc4565b9091508190610cb6565b90504210158561091d565b346100c95760a03660031901126100c957610d0d61127d565b6001600160401b036024358181116100c957610d2d903690600401611320565b6044358281116100c957610d45903690600401611320565b906064358381116100c957610d5e903690600401611320565b608435946001600160a01b031693338514158061111b575b61024057845f5260205f81526003908160405f2001548015159081611110575b506106e65760405192610da884611293565b85845281840190878252604085019186835260608601938b85528a5f525f815260405f2096519081518481116105625780610de38a5461138a565b93601f948581116110c4575b508390858311600114611062575f92611057575b50508160011b915f19908a1b1c19161788555b6001928389019051805190868211610562578190610e34845461138a565b868111611009575b508490868311600114610fab575f92610fa0575b50505f19828b1b1c191690851b1790555b60028801945190815194851161056257610e7b865461138a565b838111610f5d575b5080928511600114610ee0575092806104939997959381935f805160206120228339815191529e9f9a98965f94610ed5575b50501b915f1990861b1c19161790555b51910155604051948594856114a4565b015192508f80610eb5565b9190601f949394198416865f52835f20935f905b828210610f46575050915f805160206120228339815191529d9e9997959391856104939c9a98969410610f2f575b505050811b019055610ec5565b01515f1983881b60f8161c191690558d8080610f22565b808886978294978701518155019601940190610ef4565b865f52815f208480880160051c820192848910610f97575b0160051c019085905b828110610f8c575050610e83565b5f8155018590610f7e565b92508192610f75565b015190505f80610e50565b90879350601f19831691855f52865f20925f5b88828210610ff35750508411610fdc575b505050811b019055610e61565b01515f19838d1b60f8161c191690555f8080610fcf565b8385015186558b97909501949384019301610fbe565b909150835f52845f208680850160051c82019287861061104e575b918991869594930160051c01915b828110611040575050610e3c565b5f8155859450899101611032565b92508192611024565b015190508f80610e03565b5f8c81528581209350601f198516905b868282106110ae575050908460019594939210611097575b505050811b018855610e16565b01515f19838c1b60f8161c191690558f808061108a565b6001859682939686015181550195019301611072565b9091508a5f52835f208580850160051c820192868610611107575b9085949392910160051c01905b8181106110f95750610def565b5f81558493506001016110ec565b925081926110df565b905042101589610d96565b50604051630935e01b60e21b8152336004820152602081602481895afa9081156102b3575f9161114d575b5015610d76565b611166915060203d6020116102ac5761029e81836112e4565b87611146565b346100c95760403660031901126100c95760243580151581036100c9576100b16100c5916004356114da565b346100c95760803660031901126100c9576111b161127d565b6001600160401b036024358181116100c9576111d1903690600401611320565b906044358181116100c9576111ea903690600401611320565b906064359081116100c957611203903690600401611320565b916001600160a01b038416338114159081611227575b506102405761089c9361188f565b604051630935e01b60e21b81523360048201529150602090829060249082905afa9081156102b3575f9161125e575b501585611219565b611277915060203d6020116102ac5761029e81836112e4565b85611256565b600435906001600160a01b03821682036100c957565b608081019081106001600160401b0382111761056257604052565b606081019081106001600160401b0382111761056257604052565b602081019081106001600160401b0382111761056257604052565b90601f801991011681019081106001600160401b0382111761056257604052565b6001600160401b03811161056257601f01601f191660200190565b81601f820112156100c95780359061133782611305565b9261134560405194856112e4565b828452602083830101116100c957815f926020809301838601378301015290565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90600182811c921680156113b8575b60208310146113a457565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611399565b9060405191825f82546113d48161138a565b908184526020946001916001811690815f146114425750600114611404575b505050611402925003836112e4565b565b5f90815285812095935091905b81831061142a57505061140293508201015f80806113f3565b85548884018501529485019487945091830191611411565b9250505061140294925060ff191682840152151560051b8201015f80806113f3565b939261149f906114915f9461148360609560808a5260808a0190611366565b9088820360208a0152611366565b908682036040880152611366565b930152565b94939261149160609361148361149f9460808a5260808a0190611366565b908160209103126100c9575180151581036100c95790565b90156115ec57335f526020905f825260405f20906040516114fa81611293565b611503836113c2565b81526001906003611516600186016113c2565b94868301958652611529600282016113c2565b60408401520154606082015251908151156100c95791849261154a82611cab565b9181602161155a60018601611c79565b94850101905b6115b6575b5050508290816115b39551916040519785899651918291018488015e8501908282015f8152815193849201905e01908282015f8152815193849201905e015f838201520380845201826112e4565b90565b5f190190600a906f181899199a1a9b1b9c1cb0b131b232b360811b8282061a8353049182156115e757919082611560565b611565565b6115b39061163b565b81601f820112156100c95780519061160c82611305565b9261161a60405194856112e4565b828452602083830101116100c957815f9260208093018386015e8301015290565b335f5260019060209160018352604090815f209282519161165b836112ae565b611664856113c2565b83526116846002611677600188016113c2565b96888601978852016113c2565b84840190808252865151159081611885575b506100c95784516306fdde0360e01b8152935f85600481335afa94851561187b57908895949392915f95611832575b5051965190519192916116d791611d85565b916116e182611cab565b918160216116f160018601611c79565b94850101905b6117fc575b50505060446117b393856115b3988195828a996117ae97603d9c51998a97693d913730b6b2911d101160b11b828a0152805191829101602a8a015e87019061202360f01b602a830152805192839101602c83015e01631116101160e11b9384602c8301526e3232b9b1b934b83a34b7b7111d101160891b6030830152805192839101603f83015e0191603f830152805192839101604383015e01607d60f81b60438201520360248101845201826112e4565b611ec5565b90519384917f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000828401528051918291018484015e81015f8382015203601d8101845201826112e4565b5f190190600a906f181899199a1a9b1b9c1cb0b131b232b360811b8282061a83530491821561182d579190826116f7565b6116fc565b91955093503d805f833e61184681836112e4565b81019488828703126100c9578151916001600160401b0383116100c95789966116d79361187392016115f5565b9490916116c5565b86513d5f823e3d90fd5b905051155f611696565b906040519061189d826112ae565b81526020810183815284604083015260018060a01b0383165f52600160205260405f209082518051906001600160401b0382116105625781906118e0855461138a565b601f8111611c29575b50602090601f8311600114611bc5575f92611bba575b50508160011b915f199060031b1c19161782555b51805160018301916001600160401b03821161056257611933835461138a565b601f8111611b75575b50602090601f8311600114611b0b57918060029492604096945f92611b00575b50508160011b915f199060031b1c19161790555b019101518051906001600160401b0382116105625761198f835461138a565b601f8111611abb575b50602090601f8311600114611a2d579282611a1d93611a0496937faeb55777aebd643816b537b1251e9b2d6ec76dacb36ac8991ec89c41953fd7e098965f92611a22575b50508160011b915f199060031b1c19161790555b604051938493604085526040850190611366565b83810360208501526001600160a01b0390911695611366565b0390a2565b015190505f806119dc565b90601f19831691845f5260205f20925f5b818110611aa3575093611a0496937faeb55777aebd643816b537b1251e9b2d6ec76dacb36ac8991ec89c41953fd7e098969360019383611a1d9810611a8b575b505050811b0190556119f0565b01515f1960f88460031b161c191690555f8080611a7e565b92936020600181928786015181550195019301611a3e565b835f5260205f20601f840160051c81019160208510611af6575b601f0160051c01905b818110611aeb5750611998565b5f8155600101611ade565b9091508190611ad5565b015190505f8061195c565b90601f19831691845f5260205f20925f5b818110611b5d5750926001928592604098966002989610611b45575b505050811b019055611970565b01515f1960f88460031b161c191690555f8080611b38565b92936020600181928786015181550195019301611b1c565b835f5260205f20601f840160051c81019160208510611bb0575b601f0160051c01905b818110611ba5575061193c565b5f8155600101611b98565b9091508190611b8f565b015190505f806118ff565b5f868152602081209350601f198516905b818110611c115750908460019594939210611bf9575b505050811b018255611913565b01515f1960f88460031b161c191690555f8080611bec565b92936020600181928786015181550195019301611bd6565b909150845f5260205f20601f840160051c81019160208510611c6f575b90601f859493920160051c01905b818110611c6157506118e9565b5f8155849350600101611c54565b9091508190611c46565b90611c8382611305565b611c9060405191826112e4565b8281528092611ca1601f1991611305565b0190602036910137565b5f907a184f03e93ff9f4daa797ed6e38ed64bf6a1f01000000000000000080821015611d78575b50600a906d04ee2d6d415b85acef810000000080821015611d6b575b50662386f26fc1000080821015611d5e575b506305f5e10080821015611d51575b5061271080821015611d44575b506064811015611d36575b1015611d305790565b60010190565b606460029104920191611d27565b600491049201915f611d1c565b600891049201915f611d0f565b601091049201915f611d00565b602091049201915f611cee565b604092509004600a611cd2565b805115159082511515918080611ebe575b611e4a57611e025750611db65750604051611db0816112c9565b5f815290565b6115b360326020926040519384917030b734b6b0ba34b7b72fbab936111d101160791b82840152805191829101603184015e8101601160f91b60318201520360128101845201826112e4565b602092506115b39150602a906040519384916834b6b0b3b2911d101160b91b82840152805191829101602984015e8101601160f91b602982015203600a8101845201826112e4565b506115b39150603f90602080946040519586936834b6b0b3b2911d101160b91b82860152805191829101602986015e830190741116101130b734b6b0ba34b7b72fbab936111d101160591b6029830152805192839101603e83015e01601160f91b603e82015203601f8101845201826112e4565b5082611d96565b80511561201457604051611ed8816112ae565b604081527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208201527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f604082015281519160029260028101809111612000576003908190046001600160fe1b038116810361200057611f5f906002959492951b611c79565b936020850193839284518501935b848110611fad575050505050600390510680600114611f9b57600214611f91575090565b603d905f19015390565b50603d90815f19820153600119015390565b8360049197929394959701918251600190603f9082828260121c16880101518453828282600c1c16880101518385015382828260061c168801015188850153168501015186820153019593929190611f6d565b634e487b7160e01b5f52601160045260245ffd5b50604051611db0816112c956fe5eff125d5659803f33dbda215d6e8bfe0a404fd213a9ecb5e61973ee16cb17e7a2646970667358221220a44770d6a69e8235dcbfc6961c4e1d883363aaed265dfd299161f3504a7c3e0f64736f6c63430008190033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.