ETH Price: $3,290.68 (+3.23%)
Gas: 4 Gwei

Contract

0x65419C1eeC692E3D30FcB9E99889C6B8C723226A
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040194097132024-03-11 5:17:11125 days ago1710134231IN
 Create: SourceChainNativeMetadataStack
0 ETH0.1553994839.10883948

Advanced mode:
Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SourceChainNativeMetadataStack

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 1000 runs

Other Settings:
paris EvmVersion
File 1 of 29 : SourceChainNativeMetadataStack.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;

// Local References
import './SourceChainNativeMetadataStackBase.sol';
import './ToDynamic.sol';

/**
 * @title SourceChainNativeMetadataStack
 *
 *  ░▒▓███████▓▒░░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░ ░▒▓██████▓▒░░▒▓████████▓▒░
 * ░▒▓█▓▒░      ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
 * ░▒▓█▓▒░      ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░      ░▒▓█▓▒░
 *  ░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░░▒▓█▓▒░      ░▒▓██████▓▒░
 *        ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░      ░▒▓█▓▒░
 *        ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
 * ░▒▓███████▓▒░ ░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░░▒▓████████▓▒░
 *
 * @dev Thanks to Patrick Gillespie (@patorjk) for Text to ASCII Art Generator (TAAG).
 */
contract SourceChainNativeMetadataStack is SourceChainNativeMetadataStackBase {
    address private constant DEFAULT_ART_CONTRACT = 0xe3b57ed893E8FA03DD63A441372735bD7C07BCf5; // Mainnet v1
    address private constant DEFAULT_TYPE_MAPPER_CONTRACT = 0xB02C0e126DB84946DFe31F1f6f62D1a2A10a61A9; // Mainnet v1
    address private constant DEFAULT_ATTRIBUTE_PROVIDER_CONTRACT = 0x6Fd38Fb47436F069E461349755e4E6327660E0A3; // Mainnet v1
    uint256 private constant NUMBER_OF_TOKEN_TYPES_ALLOWED = 2; // Prereveal / Postreveal attributes
    string private constant EXTERNAL_URL = 'https://nft.christies.com/features/source';

    constructor()
        DedicatedTemplateMetadataManager(NUMBER_OF_TOKEN_TYPES_ALLOWED)
        SingletonArtConsumer(DEFAULT_ART_CONTRACT)
        SingletonMapperConsumer(DEFAULT_TYPE_MAPPER_CONTRACT)
        SourceChainNativeMetadataStackBase(DEFAULT_ATTRIBUTE_PROVIDER_CONTRACT)
    {
        // Implementation version: v1.0.0
    }

    /**
     *  struct DynamicAttributesV2 {
     *      uint64 tokenType;
     *      bool isSerialized;
     *      bool isAnimated;
     *      string title;
     *      string[] tokenDescriptionParts; // Must align with Collection._tokenDescriptionInsertionIndexes[]
     *      string[] attributeValues; // Must align with Collection.attributeNames[]
     *  }
     */
    function _getInitialDefinition()
        internal
        pure
        override
        returns (
            CollectionMetadata.CollectionAttributesV2 memory collectionAttributesDefinition,
            IDynamicAttributes.DynamicAttributesV2[] memory tokenAttributesDefinitions
        )
    {
        string[] memory shared_description_parts = ToDynamic.string_3(
            [
                'Unique output from _SOURCE [On NFTs]_, commissioned by TASCHEN, and released by Christies as the generative artistic project to come out of _On NFTs_, the largest art historical study on NFTs to date. Referencing the importance of blockchains as a new form of publishing, and the idea of NFTs as fundamentally made out of text, the works poetically distill the pre-history of NFTs into large contemporary digital color fields paintings, mediated via machine learning and NLP. Playing with the notion of digital graffiti, the works remix the color field format as a celebration of RGB and of NFTs as permanent mark making on chain. ',
                '',
                ' EXPLORE MORE AT [ROBERTALICE.COM](https://robertalice.com) '
            ]
        );
        uint16[] memory token_description_insertion_indexes = ToDynamic.uint16_1([uint16(1)]);

        string[] memory attribute_names = ToDynamic.string_16(
            [
                'STATUS',
                'TEXT[URE]',
                'UNDER TEXT[URE]',
                'On DNA',
                'On Ghosts',
                'On Masks',
                'On Plaintext',
                'On Webs',
                'Color',
                'Color Fields',
                'Library',
                'Counter Library',
                'Source',
                'Counter Source',
                'Date',
                'Counter Date'
            ]
        );

        collectionAttributesDefinition = CollectionMetadata.CollectionAttributesV2(
            EXTERNAL_URL,
            attribute_names, // Superset of Attribute Names
            shared_description_parts,
            token_description_insertion_indexes
        );

        // Token Initialization
        string[] memory previewTokenDescParts = ToDynamic.string_1(
            [' This token is still being preserved on the blockchain...']
        );
        string[] memory previewTokenAttrValues = ToDynamic.string_1(['Updating']); // Empty values will be set based on chosen traits.

        string[] memory revealTokenDescParts = ToDynamic.string_1(
            [' This token is preserved on the Ethereum blockchain.']
        );
        string[] memory revealTokenAttrValues = ToDynamic.string_1(['Rendered']); // Empty values will be set based on chosen traits.

        tokenAttributesDefinitions = new IDynamicAttributes.DynamicAttributesV2[](2);

        tokenAttributesDefinitions[0] = IDynamicAttributes.DynamicAttributesV2(
            1,
            true,
            true,
            '#', // Title will display as "#42" as an example.
            previewTokenDescParts,
            previewTokenAttrValues
        );

        tokenAttributesDefinitions[1] = IDynamicAttributes.DynamicAttributesV2(
            2,
            true,
            true,
            '#', // Title will display as "#42" as an example.
            revealTokenDescParts,
            revealTokenAttrValues
        );

        return (collectionAttributesDefinition, tokenAttributesDefinitions);
    }
}

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

/**
 * @title OwnableDeferral
 * @author @NiftyMike | @NFTCulture
 * @dev Implements checks for contract admin operations. Will be Backed by
 * OZ Ownable.
 *
 * This contract is helpful when a contract tree gets complicated,
 * and multiple contracts need to leverage Ownable.
 *
 * Sample Implementation:
 *
 * modifier isOwner() override(...) {
 *     _isOwner();
 *     _;
 * }
 *
 * function _isOwner() internal view override(...) {
 *     _checkOwner();
 * }
 */
abstract contract OwnableDeferral {
    modifier isOwner() virtual;

    function _isOwner() internal view virtual;
}

File 3 of 29 : OwnableDeferralResolution.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// OZ Libraries
import '@openzeppelin/contracts/access/Ownable.sol';

// Local References
import './OwnableDeferral.sol';

// Error Codes
error CallerIsNotOwner();

/**
 * @title OwnableDeferralResolution
 * @author @NiftyMike | @NFTCulture
 * @dev Implements checks for contract admin (Owner) operations. Backed by OZ Ownable.
 *
 * Ownership is assigned to contract deployer wallet by default.
 *
 * NOTE: IMPORTANT - This resolution will work great in a simple inheritance situation,
 * however, if multiple inheritance is involved, it might not adequately satisfy
 * override (...) conditions. In those scenarios, this code should be used as a
 * starting point and then adjusted appropriately.
 */
contract OwnableDeferralResolution is Ownable, OwnableDeferral {
    modifier isOwner() virtual override {
        _isOwner();
        _;
    }

    function _isOwner() internal view virtual override {
        // Same as _checkOwner() but using error code instead of a require statement.
        if (owner() != _msgSender()) revert CallerIsNotOwner();
    }
}

File 4 of 29 : SingletonArtConsumer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// Local References
import '../../../access/v2/OwnableDeferral.sol';
import '../interfaces/AbstractArtConsumer.sol';

/**
 * @title SingletonArtConsumer
 * @author @NiftyMike | @NFTCulture
 * @dev Contract intended to be used to consume from a single on-chain source for NFT art.
 */
abstract contract SingletonArtConsumer is AbstractArtConsumer, OwnableDeferral {
    // Single external contract that manages chain-native art.
    address private _artProducer;

    constructor(address __artProducer) {
        _setArtProducer(__artProducer);
    }

    /**
     * @notice Set the on-chain art producer contract.
     * Can only be called if caller is owner.
     *
     * @param __artProducer address of the producer contract.
     */
    function setArtProducer(address __artProducer) external isOwner {
        _setArtProducer(__artProducer);
    }

    function _setArtProducer(address __artProducer) internal {
        if (__artProducer != address(0)) {
            _artProducer = __artProducer;
        }
    }

    /**
     * @notice Get an on-chain art producer contract, which in this case is a singleton, so
     * arguments aren't used.
     */
    function getArtProducer(address, uint256) external view override returns (address) {
        return _artProducer;
    }

    function _getArtProducer(address, uint256) internal view override returns (IChainNativeArtProducer) {
        return IChainNativeArtProducer(_artProducer);
    }
}

File 5 of 29 : MutableMetadataExtension.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// Local References
import '../../../access/v2/OwnableDeferral.sol';
import '../interfaces/AbstractMetadataManager.sol';

/**
 * @title MutableMetadataExtension
 * @author @NiftyMike | @NFTCulture
 * @dev
 */
abstract contract MutableMetadataExtension is AbstractMetadataManager, OwnableDeferral {
    using CollectionMetadata for CollectionMetadata.CollectionV2;

    function updateCollectionMetadata(
        address collection,
        string calldata external_url,
        string[] calldata attributeNames,
        string[] calldata sharedDescriptionParts,
        uint16[] calldata tokenDescriptionInsertionIndexes
    ) external isOwner {
        CollectionMetadata.CollectionAttributesV2 memory collectionAttributes = _getCollection(collection)
            .getCollectionData();

        collectionAttributes.external_url = external_url;
        collectionAttributes.attributeNames = attributeNames;
        collectionAttributes.sharedDescriptionParts = sharedDescriptionParts;
        collectionAttributes.tokenDescriptionInsertionIndexes = tokenDescriptionInsertionIndexes;

        _getCollection(collection).setCollectionAttributes(collectionAttributes);
    }

    function createNewTokenDefinition(
        address collection,
        uint64 tokenType,
        bool[] memory flags,
        string memory title,
        string[] memory tokenDescriptionParts,
        string[] memory attributeValues
    ) external isOwner {
        require(flags.length == 2, 'Invalid flags');
        require(
            _getCollection(collection).getCollectionData().attributeNames.length == attributeValues.length,
            'Invalid attributes'
        );

        IDynamicAttributes.DynamicAttributesV2 memory newTokenData = IDynamicAttributes.DynamicAttributesV2(
            tokenType,
            flags[0],
            flags[1],
            title,
            tokenDescriptionParts,
            attributeValues
        );

        // Will revert if tokenType already exists.
        _getCollection(collection).createTokenData(newTokenData);
    }

    function updateTokenDefinition(
        address collection,
        uint64 tokenType,
        bool[] memory flags,
        string memory title,
        string[] memory tokenDescriptionParts,
        string[] memory attributeValues
    ) external isOwner {
        require(flags.length == 2, 'Invalid flags');
        require(
            _getCollection(collection).getCollectionData().attributeNames.length == attributeValues.length,
            'Invalid attributes'
        );

        IDynamicAttributes.DynamicAttributesV2 memory newTokenData = IDynamicAttributes.DynamicAttributesV2(
            tokenType,
            flags[0],
            flags[1],
            title,
            tokenDescriptionParts,
            attributeValues
        );

        // Will revert if tokenType already exists.
        _getCollection(collection).updateTokenData(newTokenData);
    }
}

File 6 of 29 : CollectionMetadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// Local References
import './interfaces/IDynamicAttributes.sol';

// Error Codes
error NullTokenType();
error TokenTypeAlreadyCreated();
error TokenTypeDoesNotExist();
error TokenTypeWasDestroyed();

/**
 * @title CollectionMetadata
 * @author @NiftyMike | @NFTCulture
 * @dev Library that represents an on-chain repository of metadata for a collection.
 */
library CollectionMetadata {
    struct CollectionAttributesV2 {
        string external_url;
        string[] attributeNames; // Superset of Attribute Names
        string[] sharedDescriptionParts;
        uint16[] tokenDescriptionInsertionIndexes;
    }

    struct CollectionV2 {
        // These variable should never be directly accessed by users of the library.
        uint256 _maxNumberOfTypes;
        CollectionAttributesV2 _collectionAttributes;
        IDynamicAttributes.DynamicAttributesV2[] _tokenAttributes;
    }

    function initializeDefinition(
        CollectionV2 storage collection,
        uint256 maxNumberOfTypes,
        CollectionMetadata.CollectionAttributesV2 memory collectionAttributesDefinition,
        IDynamicAttributes.DynamicAttributesV2[] memory tokenAttributesDefinition
    ) internal {
        collection._maxNumberOfTypes = maxNumberOfTypes;
        setCollectionAttributes(collection, collectionAttributesDefinition);

        uint256 idx;
        for (idx; idx < tokenAttributesDefinition.length; ) {
            IDynamicAttributes.DynamicAttributesV2 memory current = tokenAttributesDefinition[idx];
            createTokenData(collection, current);

            unchecked {
                ++idx;
            }
        }
    }

    function createTokenData(
        CollectionV2 storage collection,
        IDynamicAttributes.DynamicAttributesV2 memory tokenAttributes
    ) internal {
        uint64 tokenType = tokenAttributes.tokenType;

        if (tokenType == 0) revert NullTokenType();
        if (tokenType <= collection._tokenAttributes.length) revert TokenTypeAlreadyCreated();

        collection._tokenAttributes.push(tokenAttributes);
    }

    function existsTokenData(CollectionV2 storage collection, uint64 tokenType) internal view returns (bool) {
        return tokenType > 0 && tokenType <= collection._tokenAttributes.length;
    }

    function getTokenData(
        CollectionV2 storage collection,
        uint64 tokenType
    ) internal view returns (IDynamicAttributes.DynamicAttributesV2 memory) {
        if (tokenType < 1 || tokenType > collection._tokenAttributes.length) revert TokenTypeDoesNotExist();

        return collection._tokenAttributes[tokenType - 1];
    }

    function getCollectionData(CollectionV2 storage collection) internal view returns (CollectionAttributesV2 memory) {
        return collection._collectionAttributes;
    }

    function updateTokenData(
        CollectionV2 storage collection,
        IDynamicAttributes.DynamicAttributesV2 memory tokenAttributes
    ) internal {
        if (tokenAttributes.tokenType < 1 || tokenAttributes.tokenType > collection._tokenAttributes.length)
            revert TokenTypeDoesNotExist();
        if (collection._tokenAttributes[tokenAttributes.tokenType - 1].tokenType == 0) revert TokenTypeWasDestroyed();

        collection._tokenAttributes[tokenAttributes.tokenType - 1] = tokenAttributes;
    }

    function setCollectionAttributes(
        CollectionV2 storage collection,
        CollectionAttributesV2 memory collectionAttributes
    ) internal {
        collection._collectionAttributes = collectionAttributes;
    }

    function destroy() internal pure {
        // Not Implemented
    }
}

File 7 of 29 : AbstractArtConsumer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// Local References
import './IChainNativeArtConsumer.sol';
import './IChainNativeArtProducer.sol';

/**
 * @title AbstractArtConsumer
 * @author @NiftyMike | @NFTCulture
 * @dev Abstract contract that defines what an ArtConsumer should do.
 */
abstract contract AbstractArtConsumer is IChainNativeArtConsumer {
    function _getArtProducer(
        address collection,
        uint256 selectionCriteria
    ) internal view virtual returns (IChainNativeArtProducer);
}

File 8 of 29 : AbstractMetadataManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// OZ Libraries
import '@openzeppelin/contracts/utils/Base64.sol';
import '../CollectionMetadata.sol';
import '../interfaces/IChainNativeMetadataProducer.sol';
import '../interfaces/IDynamicAttributes.sol';

/**
 * @title AbstractMetadataManager
 * @author @NiftyMike | @NFTCulture
 * @dev Abstract contract that defines the functionality that should
 * be provided by a "MetadataManager" contract.
 *
 * This contract is closely related to the external interface defined by
 * IChainNativeMetadataProducer. A contract that implements that interface
 * will most likely need to use implementations for the following methods.
 *
 * The MetadataManager hierarchy can be used for dedicated stores or communalized stores, and
 * requires the passing of the collection address in order to faclitate this.
 */
abstract contract AbstractMetadataManager is IChainNativeMetadataProducer {
    using CollectionMetadata for CollectionMetadata.CollectionV2;

    function _getCollection(address collection) internal view virtual returns (CollectionMetadata.CollectionV2 storage);

    function _setCollection(
        address collection,
        uint256 maxNumberOfTypes,
        CollectionMetadata.CollectionAttributesV2 memory collectionAttributesDefinition,
        IDynamicAttributes.DynamicAttributesV2[] memory tokenAttributesDefinition
    ) internal virtual;

    /**
     * Return aa string that can be used as the value in an ERC721Metadata
     * tokenURI's Image field. It may or may not be encoded to base64.
     *
     */
    function _getImageFieldValue(
        address collection,
        uint64 tokenType,
        uint256 extraData
    ) internal view virtual returns (string memory);

    /**
     * Return aa string that can be used as the value in an ERC721Metadata
     * tokenURI's Animation field. It may or may not be encoded to base64.
     */
    function _getAnimationFieldValue(
        address collection,
        uint64 tokenType,
        uint256 extraData
    ) internal view virtual returns (string memory);

    /**
     * Return an unencoded, human readable string that can be subsequently encoded
     * and returned as an ERC721Metadata tokenURI. This exists primarily for troubleshooting
     * and human convenience.
     */
    function _getMetadataJson(
        address collection,
        uint64 tokenType,
        uint256 tokenId,
        uint256 extraData
    ) internal view virtual returns (string memory);

    /**
     * Return a dataURI that can be directly returned as an ERC721Metadata tokenURI.
     */
    function _getMetadataJsonAsDataURI(
        address collection,
        uint64 tokenType,
        uint256 tokenId,
        uint256 extraData
    ) internal view virtual returns (string memory);

    /**
     * Given a string, Base64 encode it and incorporate it into a JSON dataURI.
     */
    function _constructDataURIFromJSON(string memory metadataJSON) internal pure returns (string memory) {
        return string.concat('data:application/json;base64,', Base64.encode(bytes(metadataJSON)));
    }
}

File 9 of 29 : AbstractTokenTypeMapperConsumer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// Local References
import './IChainNativeTokenTypeMapperConsumer.sol';
import './IChainNativeTokenTypeMapperProducer.sol';

/**
 * @title AbstractTokenTypeMapperConsumer
 * @author @NiftyMike | @NFTCulture
 * @dev Abstract contract that defines what a TokenTypeMapperConsumer should do.
 */
abstract contract AbstractTokenTypeMapperConsumer is IChainNativeTokenTypeMapperConsumer {
    function _getMapperProducer(
        address collection,
        uint256 selectionCriteria
    ) internal view virtual returns (IChainNativeTokenTypeMapperProducer);
}

File 10 of 29 : IChainNativeArtConsumer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title IChainNativeArtConsumer
 * @author @NiftyMike | @NFTCulture
 * @dev Super thin interface definition for a contract that
 * consumes art from an IChainNativeArtProducer
 */
interface IChainNativeArtConsumer {
    /**
     * Given a collection address, provide an art producer.
     *
     * @param collection collection we are requesting a producer for.
     * @param selectionCriteria data pertinent to selection of the producer.
     *
     * NOTE: It is important to realize that selectionCriteria is generic and does not imply a tokenType or
     * tokenId or anything else, it depends on the circumstance of the implementation.
     */
    function getArtProducer(address collection, uint256 selectionCriteria) external view returns (address);
}

File 11 of 29 : IChainNativeArtProducer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title IChainNativeArtProducer
 * @author @NiftyMike | @NFTCulture
 * @dev Super thin interface definition for a contract that
 * produces art in a chain native way.
 */
interface IChainNativeArtProducer {
    /**
     * Given a token descriptor, return a string that can be directly inserted into an
     * NFT metadata attribute such as image.
     *
     * @param tokenType type of the art piece
     * @param extraData extra info that can be used to get the art, if applicable
     */
    function getArtAsString(uint64 tokenType, uint256 extraData) external view returns (string memory);

    /**
     * Given a token descriptor, return a string that can be directly inserted into an
     * NFT metadata attribute such as animation_url.
     *
     * @param tokenType type of the art piece
     * @param extraData extra info that can be used to get the animation, if applicable
     */
    function getAnimationAsString(uint64 tokenType, uint256 extraData) external view returns (string memory);
}

File 12 of 29 : IChainNativeMetadataProducer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title IChainNativeMetadataProducer
 * @author @NiftyMike | @NFTCulture
 * @dev Super thin interface definition for a contract that
 * produces metadata in a chain native way.
 */
interface IChainNativeMetadataProducer {
    /**
     *
     * @param collection - the collection details that we are providing info for.
     * @param tokenId - the tokenId that we should provide a tokenType for.
     */
    function getTokenTypeForToken(address collection, uint256 tokenId) external view returns (uint64);

    /**
     * Provide a human-readable version of the metadata JSON.
     *
     * @param collection - the collection details that we are providing info for.
     * @param tokenType - the type of the token we should provide metadata for
     * @param tokenId - the specific tokenId
     * @param extraData - extra info that might be used to effect the metadata
     */
    function getJsonAsString(
        address collection,
        uint64 tokenType,
        uint256 tokenId,
        uint256 extraData
    ) external view returns (string memory);

    /**
     * Provide base64 encoded metadata JSON that can be consumed by an external service.
     *
     * @param collection - the collection details that we are providing info for.
     * @param tokenType - the type of the token we should provide metadata for
     * @param tokenId - the specific tokenIds
     * @param extraData - extra info that might be used to effect the metadata
     */
    function getJsonAsEncodedString(
        address collection,
        uint64 tokenType,
        uint256 tokenId,
        uint256 extraData
    ) external view returns (string memory);
}

File 13 of 29 : IChainNativeTokenTypeMapperConsumer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title IChainNativeTokenTypeMapperConsumer
 * @author @NiftyMike | @NFTCulture
 * @dev Super thin interface definition for a contract that
 * consumes art from an IChainNativeTokenTypeMapperProducer
 */
interface IChainNativeTokenTypeMapperConsumer {
    /**
     * Given a collection address, provide a tokenType mapper producer.
     *
     * @param collection collection we are requesting a producer for.
     * @param selectionCriteria data pertinent to selection of the producer.
     *
     * NOTE: It is important to realize that selectionCriteria is generic and does not imply a tokenType or
     * tokenId or anything else, it depends on the circumstance of the implementation.
     */
    function getMapperProducer(address collection, uint256 selectionCriteria) external view returns (address);
}

File 14 of 29 : IChainNativeTokenTypeMapperProducer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title IChainNativeTokenTypeMapperProducer
 * @author @NiftyMike | @NFTCulture
 * @dev Super thin interface definition for a contract that produces
 * a tokenType value when given details about a token.
 */
interface IChainNativeTokenTypeMapperProducer {
    /**
     * Given an address and a tokenID, return the tokenType of the token.
     *
     * @param collection Address of the collection
     * @param tokenId ID of the token
     */
    function mapToTokenType(address collection, uint256 tokenId) external view returns (uint64);
}

File 15 of 29 : IDynamicAttributes.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title IDynamicAttributes
 * @author @NFTMike | @NFTCulture
 * @dev Interface for defining the structure of DynamicAttributes objects.
 *
 * This interface should capture all of the data relevant to a group of tokens being
 * stored entirely on-chain.
 *
 * The interface is designed to allow the metadata to be modified and updated as needed.
 *
 * Besides the 'isAnimated' attribute, the interface is designed to be decoupled from
 * the artwork scheme implemented for the related tokens. 'isAnimated' is just used
 * as a cleaner and more deliberate approach than checking string length of an animation.
 */
interface IDynamicAttributes {
    struct DynamicAttributesV2 {
        uint64 tokenType;
        bool isSerialized;
        bool isAnimated;
        string title;
        string[] tokenDescriptionParts; // Must align with Collection._tokenDescriptionInsertionIndexes[]
        string[] attributeValues; // Must align with Collection.attributeNames[]
    }
}

File 16 of 29 : SingletonMapperConsumer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// Local References
import '../../../access/v2/OwnableDeferral.sol';
import '../interfaces/AbstractTokenTypeMapperConsumer.sol';

/**
 * @title SingletonMapperConsumer
 * @author @NiftyMike | @NFTCulture
 * @dev Contract intended to be used to consume from a single on-chain source for tokenType mappings.
 */
abstract contract SingletonMapperConsumer is AbstractTokenTypeMapperConsumer, OwnableDeferral {
    // External contract that manages the collection's mapper in a chain-native way.
    address private _mapperProducer;

    constructor(address __mapperProducer) {
        _setMapperProducer(__mapperProducer);
    }

    /**
     * @notice Set the on-chain mapper producer contract.
     * Can only be called if caller is owner.
     *
     * @param __mapperProducer address of the producer contract.
     */
    function setMapperProducer(address __mapperProducer) external isOwner {
        _setMapperProducer(__mapperProducer);
    }

    function _setMapperProducer(address __mapperProducer) internal {
        if (__mapperProducer != address(0)) {
            _mapperProducer = __mapperProducer;
        }
    }

    function getMapperProducer(address, uint256) external view override returns (address) {
        return _mapperProducer;
    }

    function _getMapperProducer(address, uint256) internal view override returns (IChainNativeTokenTypeMapperProducer) {
        return IChainNativeTokenTypeMapperProducer(_mapperProducer);
    }
}

File 17 of 29 : DedicatedTemplateMetadataManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// Local References
import '../CollectionMetadata.sol';
import './TemplateMetadataManager.sol';

/**
 * @title DedicatedTemplateMetadataManager
 * @author @NiftyMike | @NFTCulture
 * @dev Extension that provides management functionality for an individual NFT collection.
 */
abstract contract DedicatedTemplateMetadataManager is TemplateMetadataManager {
    using CollectionMetadata for CollectionMetadata.CollectionV2;

    CollectionMetadata.CollectionV2 private _dedicatedMetadataStore;

    constructor(uint256 maxNumberOfTypes) {
        (
            CollectionMetadata.CollectionAttributesV2 memory collectionAttributesDefinition,
            IDynamicAttributes.DynamicAttributesV2[] memory tokenAttributesDefinitions
        ) = _getInitialDefinition();

        _setCollection(address(0), maxNumberOfTypes, collectionAttributesDefinition, tokenAttributesDefinitions);
    }

    function _getInitialDefinition()
        internal
        virtual
        returns (
            CollectionMetadata.CollectionAttributesV2 memory collectionAttributesDefinition,
            IDynamicAttributes.DynamicAttributesV2[] memory tokenAttributesDefinitions
        );

    function _getCollection(address) internal view override returns (CollectionMetadata.CollectionV2 storage) {
        // Ignore the collection address, since this one is dedicated to one collection.
        return _dedicatedMetadataStore;
    }

    function _setCollection(
        address,
        uint256 maxNumberOfTypes,
        CollectionMetadata.CollectionAttributesV2 memory collectionAttributesDefinition,
        IDynamicAttributes.DynamicAttributesV2[] memory tokenAttributesDefinition
    ) internal override {
        _dedicatedMetadataStore.initializeDefinition(
            maxNumberOfTypes,
            collectionAttributesDefinition,
            tokenAttributesDefinition
        );
    }
}

File 18 of 29 : MutableDedicatedTemplateMetadataManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// Local References
import './DedicatedTemplateMetadataManager.sol';
import '../collection-extensions/MutableMetadataExtension.sol';

/**
 * @title MutableDedicatedTemplateMetadataManager
 * @author @NiftyMike | @NFTCulture
 * @dev Extension to DedicatedTemplateMetadataManager that adds mutability.
 */
abstract contract MutableDedicatedTemplateMetadataManager is
    MutableMetadataExtension,
    DedicatedTemplateMetadataManager
{
    // Tag contract
}

File 19 of 29 : TemplateMetadataManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// OZ Libraries
import '@openzeppelin/contracts/utils/Strings.sol';

// Local References
import '../CollectionMetadata.sol';
import '../interfaces/AbstractMetadataManager.sol';

/**
 * @title TemplateMetadataManager
 * @author @NiftyMike | @NFTCulture
 * @dev Minimal implementation, for more customizability.
 */
abstract contract TemplateMetadataManager is AbstractMetadataManager {
    using Strings for uint256;
    using CollectionMetadata for CollectionMetadata.CollectionV2;

    function _getMetadataJson(
        address collection,
        uint64 tokenType,
        uint256 tokenId,
        uint256 extraData
    ) internal view virtual override returns (string memory) {
        return _constructMetadataAsJSON(collection, tokenType, tokenId, extraData);
    }

    function _getMetadataJsonAsDataURI(
        address collection,
        uint64 tokenType,
        uint256 tokenId,
        uint256 extraData
    ) internal view virtual override returns (string memory) {
        return _constructDataURIFromJSON(_constructMetadataAsJSON(collection, tokenType, tokenId, extraData));
    }

    function _constructMetadataAsJSON(
        address collection,
        uint64 tokenType,
        uint256 tokenId,
        uint256 extraData
    ) internal view virtual returns (string memory);

    function _constructDescriptionString(
        CollectionMetadata.CollectionAttributesV2 memory collectionData,
        IDynamicAttributes.DynamicAttributesV2 memory tokenAttributes,
        uint64,
        uint256,
        uint256
    ) internal view virtual returns (string memory description);

    function _constructAttributeString(
        CollectionMetadata.CollectionAttributesV2 memory collectionData,
        IDynamicAttributes.DynamicAttributesV2 memory tokenAttributes,
        uint64,
        uint256 tokenId,
        uint256
    ) internal view virtual returns (string memory attributeArrayAsString);
}

File 20 of 29 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 21 of 29 : Base64.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.6) (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, 0x20)
            let dataPtr := data
            let endPtr := add(data, mload(data))

            // In some cases, the last iteration will read bytes after the end of the data. We cache the value, and
            // set it to zero to make sure no dirty bytes are read in that section.
            let afterPtr := add(endPtr, 0x20)
            let afterCache := mload(afterPtr)
            mstore(afterPtr, 0x00)

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

            } lt(dataPtr, endPtr) {

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

                // To write each character, shift the 3 byte (24 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F to bitmask the least significant 6 bits.
                // Use this as an index into the lookup table, mload an entire word
                // so the desired character is in the least significant byte, and
                // mstore8 this least significant byte into the result and continue.

                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
            }

            // Reset the value that was cached
            mstore(afterPtr, afterCache)

            // 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 22 of 29 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 23 of 29 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 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 256, 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 << 3) < value ? 1 : 0);
        }
    }
}

File 24 of 29 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 25 of 29 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.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 `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

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

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 26 of 29 : SourceChainNativeMetadataStackBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

// NFTC Prerelease Contracts
import '@nftculture/nftc-contracts-private/contracts/access/v2/OwnableDeferralResolution.sol';
import '@nftculture/nftc-contracts-private/contracts/metadata/v2/art-consumers/SingletonArtConsumer.sol';
import '@nftculture/nftc-contracts-private/contracts/metadata/v2/mapping-consumers/SingletonMapperConsumer.sol';
import '@nftculture/nftc-contracts-private/contracts/metadata/v2/metadata-managers/MutableDedicatedTemplateMetadataManager.sol';

// Local References
import '../IAttributeProvider.sol';
import '../LibMath.sol';

error TokenTypeNotFound(uint64);

/**
 * @title SourceChainNativeMetadataStackBase
 * @author @NiftyMike | @NFTCulture
 * @dev This contract implements the IChainNativeMetadataProducer API, which means it
 * contains logic to return the ERC721Metadata JSON data pertinent to an ERC721 NFT, in
 * a fully on-chain fashion.
 *
 * This particular implementation is a TemplateMetadataManager, which means that this
 * class provides implementation for most of the direct chain native functionality itself
 * and it only leverages the parent classes for the general pattern provided for on-chain
 * metadata.
 *
 * This particular implementation is also Dedicated which means it is designed
 * to only hose metadata for one single NFT project, and Mutable, which means that the
 * Metadata can be adjusted via write transactions in the event that something needs
 * to be altered.
 *
 * This contract has dependencies on an externally defined Art Manager, which provides the
 * art assets it delivers in the image and animation_url fields. It also depends on a
 * Type Mapper contract which tells it how to assign tokenIds to incoming JSON requests.
 *
 * This version of MetadataManager also has a special design to account for deferred
 * designation of NFT attributes. During the initial phase of the mint, it will provide
 * a temporary placeholder set of attributes. Once the initial phase as completed, it
 * will transition to use an external attribute provider.
 */
abstract contract SourceChainNativeMetadataStackBase is
    MutableDedicatedTemplateMetadataManager, // We provide metadata for a single collection.
    SingletonArtConsumer, // All art will be produced by a single contract.
    SingletonMapperConsumer, // All mappings will be produced by a single contract.
    OwnableDeferralResolution
{
    using Strings for uint256;
    using CollectionMetadata for CollectionMetadata.CollectionV2;
    using LibMath for bool;

    address private _attributeProvider;

    constructor(address __attributeProvider) {
        _setAttributeProvider(__attributeProvider);
    }

    function getAttributeProvider() external view returns (address) {
        return _attributeProvider;
    }

    function _getAttributeProvider() internal view returns (IAttributeProvider attributeProvider) {
        return IAttributeProvider(_attributeProvider);
    }

    function setAttributeProvider(address __attributeProvider) external isOwner {
        _setAttributeProvider(__attributeProvider);
    }

    function _setAttributeProvider(address __attributeProvider) internal {
        _attributeProvider = __attributeProvider;
    }

    function getTokenTypeForToken(address collection, uint256 tokenId) external view override returns (uint64) {
        // Query mapper for our token type.
        return _getMapperProducer(collection, tokenId).mapToTokenType(collection, tokenId);
    }

    function getJsonAsString(
        address collection,
        uint64 tokenType,
        uint256 tokenId,
        uint256 extraData
    ) external view override returns (string memory) {
        return _getMetadataJson(collection, tokenType, tokenId, extraData);
    }

    function getJsonAsEncodedString(
        address collection,
        uint64 tokenType,
        uint256 tokenId,
        uint256 extraData
    ) external view override returns (string memory) {
        return _getMetadataJsonAsDataURI(collection, tokenType, tokenId, extraData);
    }

    function _getImageFieldValue(
        address collection,
        uint64 tokenType,
        uint256 extraData
    ) internal view override returns (string memory) {
        return _getArtProducer(collection, tokenType).getArtAsString(tokenType, extraData);
    }

    function _getAnimationFieldValue(
        address collection,
        uint64 tokenType,
        uint256 extraData
    ) internal view override returns (string memory) {
        return _getArtProducer(collection, tokenType).getAnimationAsString(tokenType, extraData);
    }

    function _constructMetadataAsJSON(
        address collection,
        uint64 tokenType,
        uint256 tokenId,
        uint256 extraDataAsEntropy
    ) internal view override returns (string memory metadataAsJSON) {
        // Prerender Phase: Display placeholder image, from api-server.
        // Postrender Phase / During Mint: Display preview image, from api-server.
        // Archive Phase: Display preview image, from ARWeave.
        IDynamicAttributes.DynamicAttributesV2 memory tokenAttributes = _getCollection(collection).getTokenData(
            _applyModeMask(_areTraitsMapped(tokenId))
        );
        if (tokenAttributes.tokenType == 0) revert TokenTypeNotFound(tokenType);

        CollectionMetadata.CollectionAttributesV2 memory collectionData = _getCollection(collection)
            .getCollectionData();

        metadataAsJSON = string.concat(
            '{"name":"',
            tokenAttributes.title,
            tokenAttributes.isSerialized ? uint256(uint16(tokenId)).toString() : '',
            '","description":"',
            _constructDescriptionString(collectionData, tokenAttributes, tokenType, uint16(tokenId), extraDataAsEntropy)
        );

        metadataAsJSON = string.concat(
            metadataAsJSON,
            '","image":"',
            _getImageFieldValue(collection, tokenType, tokenId), // Pass full tokenId encoding to the image function.
            tokenAttributes.isAnimated ? '","animation_url":"' : '',
            tokenAttributes.isAnimated ? _getAnimationFieldValue(collection, tokenType, extraDataAsEntropy) : '',
            '","attributes":',
            _constructAttributeString(
                collectionData,
                tokenAttributes,
                _applyModeMask(_areTraitsMapped(tokenId)),
                uint16(tokenId),
                tokenId >> 16
            ),
            ',"external_url":"',
            collectionData.external_url,
            '"}'
        );
    }

    function _constructDescriptionString(
        CollectionMetadata.CollectionAttributesV2 memory collectionData,
        IDynamicAttributes.DynamicAttributesV2 memory tokenAttributes,
        uint64,
        uint256,
        uint256
    ) internal pure override returns (string memory description) {
        uint256 descriptionIdx;
        uint256 insertionIdx;
        for (descriptionIdx; descriptionIdx < collectionData.sharedDescriptionParts.length; descriptionIdx++) {
            if (bytes(collectionData.sharedDescriptionParts[descriptionIdx]).length == 0) {
                if (
                    collectionData.tokenDescriptionInsertionIndexes.length > insertionIdx &&
                    collectionData.tokenDescriptionInsertionIndexes[insertionIdx] == descriptionIdx
                ) {
                    // The description part is empty, and we are at an insertion index...
                    description = string.concat(description, tokenAttributes.tokenDescriptionParts[insertionIdx]);
                    insertionIdx++;
                } else {
                    // Not an insertion point. This slot is just blank.
                }
            } else {
                description = string.concat(description, collectionData.sharedDescriptionParts[descriptionIdx]);
            }
        }
    }

    uint256 private constant PLACEHOLDER_FIELD_COUNT = 1;

    function _constructAttributeString(
        CollectionMetadata.CollectionAttributesV2 memory collectionData,
        IDynamicAttributes.DynamicAttributesV2 memory tokenAttributes,
        uint64 tokenType,
        uint256,
        uint256 traitMap
    ) internal view override returns (string memory attributeArrayAsString) {
        attributeArrayAsString = '[';

        uint256 tokenAttrIdx;
        if (tokenType == 1) {
            // Status Field Only
            for (; tokenAttrIdx < PLACEHOLDER_FIELD_COUNT; tokenAttrIdx++) {
                attributeArrayAsString = string.concat(
                    attributeArrayAsString,
                    tokenAttrIdx == 0 ? '' : ',',
                    '{"trait_type":"',
                    collectionData.attributeNames[tokenAttrIdx],
                    '","value":"',
                    tokenAttributes.attributeValues[tokenAttrIdx],
                    '"}'
                );
            }
        } else if (tokenType == 2) {
            // Rendered fields Only
            for (
                tokenAttrIdx = PLACEHOLDER_FIELD_COUNT;
                tokenAttrIdx < collectionData.attributeNames.length;
                tokenAttrIdx++
            ) {
                attributeArrayAsString = string.concat(
                    attributeArrayAsString,
                    tokenAttrIdx == PLACEHOLDER_FIELD_COUNT ? '' : ',',
                    '{"trait_type":"',
                    collectionData.attributeNames[tokenAttrIdx],
                    '","value":"',
                    _getTraitValueFromAttribute(tokenAttrIdx - PLACEHOLDER_FIELD_COUNT, traitMap),
                    '"}'
                );
            }
        }

        attributeArrayAsString = string.concat(attributeArrayAsString, ']');
    }

    function _getTraitValueFromAttribute(uint256 tokenAttrIdx, uint256 traitMap) internal view returns (string memory) {
        return _getAttributeProvider().getAttributeValueFor(tokenAttrIdx, _getValueFromByte(traitMap, tokenAttrIdx));
    }

    function _getValueFromByte(uint256 _packedBytes, uint256 _byteIndex) internal pure returns (uint8) {
        uint256 shiftAmount = 8 * _byteIndex;

        // 255 (0xFF) is the mask to isolate one byte
        uint256 byteValue = (_packedBytes >> shiftAmount) & uint256(255);
        return uint8(byteValue);
    }

    function _areTraitsMapped(uint256 tokenId) internal pure returns (bool) {
        // Returns true if TraitMap has been encoded into the tokenId.
        return (tokenId >> 16) > 0;
    }

    function _applyModeMask(bool mapped) internal pure returns (uint64) {
        // inspired by curta.wtf
        return (uint64(mapped.toUint256()) | 1) + uint64(mapped.toUint256());
    }
}

File 27 of 29 : ToDynamic.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title ToDynamic
 * @author An unnamed faceless developer's older brother.
 * @dev Hardcoded array converters. These allow conversion of static sized arrays to dynamic, which allows
 * cheaper fixed-size memory variables to be used but still interface with functions that require dynamically
 * allocated input params.
 */
library ToDynamic {
    function string_1(string[1] memory fixedArray) internal pure returns (string[] memory dynamicArray) {
        dynamicArray = new string[](1);
        dynamicArray[0] = fixedArray[0];
    }

    function uint16_1(uint16[1] memory fixedArray) internal pure returns (uint16[] memory dynamicArray) {
        dynamicArray = new uint16[](1);
        dynamicArray[0] = fixedArray[0];
    }

    function string_16(string[16] memory fixedArray) internal pure returns (string[] memory dynamicArray) {
        dynamicArray = new string[](16);
        uint256 i;
        for (; i < 16; ) {
            dynamicArray[i] = fixedArray[i];

            unchecked {
                i++;
            }
        }
    }

    function string_3(string[3] memory fixedArray) internal pure returns (string[] memory dynamicArray) {
        dynamicArray = new string[](3);
        uint256 i;
        for (; i < 3; ) {
            dynamicArray[i] = fixedArray[i];

            unchecked {
                i++;
            }
        }
    }
}

File 28 of 29 : IAttributeProvider.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

/**
 * @title IAttributeProvider
 * @author @NiftyMike | @NFTCulture
 * @dev Super thin interface that describes a function that provides attribute information.
 */
interface IAttributeProvider {
    /**
     * Lookup an attribute value given the trait and the index of the attribute.
     *
     * @param traitIndex - the index of the trait
     * @param attributeIndex - the index of the selected attribute.
     */
    function getAttributeValueFor(uint256 traitIndex, uint8 attributeIndex) external view returns (string memory);
}

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

/**
 * @title LibMath
 * @author An unnamed, faceless developer.
 */
library LibMath {
    /**
     * @dev Converts a boolean value to its uint256 representation.
     * @param x - The boolean value to convert.
     * @return r - The uint256 representation of the input boolean.
     */
    function toUint256(bool x) internal pure returns (uint256 r) {
        assembly {
            r := x
        }
    }

    /**
     * Converts a string to a uint256.
     *
     * @dev - Credit to bonis.tech for first pass of implementation.
     * @param str - the string to convert
     */
    function stringToUint256(string memory str) internal pure returns (uint256 result) {
        bytes memory strAsBytes = bytes(str);
        uint256 idx;
        for (; idx < strAsBytes.length; ) {
            uint256 char = uint256(uint8(strAsBytes[idx]));
            if (char >= 48 && char <= 57) {
                result = result * 10 + (char - 48);
            }

            unchecked {
                idx++;
            }
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallerIsNotOwner","type":"error"},{"inputs":[],"name":"NullTokenType","type":"error"},{"inputs":[],"name":"TokenTypeAlreadyCreated","type":"error"},{"inputs":[],"name":"TokenTypeDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"TokenTypeNotFound","type":"error"},{"inputs":[],"name":"TokenTypeWasDestroyed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint64","name":"tokenType","type":"uint64"},{"internalType":"bool[]","name":"flags","type":"bool[]"},{"internalType":"string","name":"title","type":"string"},{"internalType":"string[]","name":"tokenDescriptionParts","type":"string[]"},{"internalType":"string[]","name":"attributeValues","type":"string[]"}],"name":"createNewTokenDefinition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getArtProducer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAttributeProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint64","name":"tokenType","type":"uint64"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"extraData","type":"uint256"}],"name":"getJsonAsEncodedString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint64","name":"tokenType","type":"uint64"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"extraData","type":"uint256"}],"name":"getJsonAsString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getMapperProducer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getTokenTypeForToken","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"__artProducer","type":"address"}],"name":"setArtProducer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"__attributeProvider","type":"address"}],"name":"setAttributeProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"__mapperProducer","type":"address"}],"name":"setMapperProducer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"string","name":"external_url","type":"string"},{"internalType":"string[]","name":"attributeNames","type":"string[]"},{"internalType":"string[]","name":"sharedDescriptionParts","type":"string[]"},{"internalType":"uint16[]","name":"tokenDescriptionInsertionIndexes","type":"uint16[]"}],"name":"updateCollectionMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint64","name":"tokenType","type":"uint64"},{"internalType":"bool[]","name":"flags","type":"bool[]"},{"internalType":"string","name":"title","type":"string"},{"internalType":"string[]","name":"tokenDescriptionParts","type":"string[]"},{"internalType":"string[]","name":"attributeValues","type":"string[]"}],"name":"updateTokenDefinition","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50736fd38fb47436f069e461349755e4e6327660e0a373b02c0e126db84946dfe31f1f6f62d1a2a10a61a973e3b57ed893e8fa03dd63a441372735bd7c07bcf560026200005e33620000c2565b6000806200006b62000112565b90925090506200007f600084848462000687565b50505062000093816200069c60201b60201c565b506200009f81620006cb565b50600980546001600160a01b0319166001600160a01b0383161790555062000da9565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6200013e6040518060800160405280606081526020016060815260200160608152602001606081525090565b60606000620001aa6040518060600160405280604051806102a00160405280610277815260200162003781610277913981526020016040518060200160405280600081525081526020016040518060600160405280603c815260200162003a5a603c91399052620006fb565b604080516020810190915260018152909150600090620001ca9062000781565b6040805161024081018252600661020082018181526553544154555360d01b610220840152825282518084018452600980825268544558545b5552455d60b81b6020838101919091528085019290925284518086018652600f8082526e554e44455220544558545b5552455d60881b828501528587019190915285518087018752848152654f6e20444e4160d01b81850152606086015285518087018752918252684f6e2047686f73747360b81b8284015260808501919091528451808601865260088152674f6e204d61736b7360c01b8184015260a085015284518086018652600c8082526b13db88141b185a5b9d195e1d60a21b8285015260c0860191909152855180870187526007808252664f6e205765627360c81b8286015260e087019190915286518088018852600581526421b7b637b960d91b81860152610100870152865180880188528281526b436f6c6f72204669656c647360a01b8186015261012087015286518088018852908152664c69627261727960c81b81850152610140860152855180870187529182526e436f756e746572204c69627261727960881b828401526101608501919091528451808601865292835265536f7572636560d01b8383015261018084019290925283518085018552600e81526d436f756e74657220536f7572636560901b818301526101a08401528351808501855260048152634461746560e01b818301526101c084015283518085019094529083526b436f756e746572204461746560a01b908301526101e08101919091529091506000906200041890620007d7565b90506040518060800160405280604051806060016040528060298152602001620039f8602991398152602001828152602001848152602001838152509450600062000489604051806020016040528060405180606001604052806039815260200162003a2160399139905262000858565b60408051606081018252600860208201908152675570646174696e6760c01b92820192909252908152909150600090620004c39062000858565b90506000620004f860405180602001604052806040518060600160405280603481526020016200374d60349139905262000858565b604080516060810182526008602082019081526714995b99195c995960c21b92820192909252908152909150600090620005329062000858565b60408051600280825260608201909252919250816020015b6040805160c081018252600080825260208083018290529282015260608082018190526080820181905260a082015282526000199092019101816200054a5790505097506040518060c0016040528060016001600160401b03168152602001600115158152602001600115158152602001604051806040016040528060018152602001602360f81b81525081526020018581526020018481525088600081518110620005fa57620005fa62000c40565b60200260200101819052506040518060c0016040528060026001600160401b03168152602001600115158152602001600115158152602001604051806040016040528060018152602001602360f81b8152508152602001838152602001828152508860018151811062000671576200067162000c40565b6020026020010181905250505050505050509091565b620006966001848484620008b0565b50505050565b6001600160a01b03811615620006c857600780546001600160a01b0319166001600160a01b0383161790555b50565b6001600160a01b03811615620006c857600880546001600160a01b0383166001600160a01b031990911617905550565b60408051600380825260808201909252606091816020015b60608152602001906001900390816200071357905050905060005b60038110156200077b578281600381106200074d576200074d62000c40565b602002015182828151811062000767576200076762000c40565b60209081029190910101526001016200072e565b50919050565b6040805160018082528183019092526060916020808301908036833750508351825192935091839150600090620007bc57620007bc62000c40565b602002602001019061ffff16908161ffff1681525050919050565b6040805160108082526102208201909252606091816020015b6060815260200190600190039081620007f057905050905060005b60108110156200077b578281601081106200082a576200082a62000c40565b602002015182828151811062000844576200084462000c40565b60209081029190910101526001016200080b565b604080516001808252818301909252606091816020015b60608152602001906001900390816200086f57505082518151919250908290600090620008a057620008a062000c40565b6020026020010181905250919050565b828455620008bf848362000910565b60005b815181101562000909576000828281518110620008e357620008e362000c40565b60200260200101519050620008ff86826200097e60201b60201c565b50600101620008c2565b5050505050565b805181906001840190819062000927908262000cdd565b50602082810151805162000942926001850192019062000aaa565b50604082015180516200096091600284019160209091019062000aaa565b50606082015180516200090991600384019160209091019062000b07565b80516001600160401b038116600003620009ab5760405163cc4ccb6b60e01b815260040160405180910390fd5b60058301546001600160401b03821611620009d95760405163858c1d2960e01b815260040160405180910390fd5b60058301805460018181018355600092835260209283902085516004909302018054938601516040870151151569010000000000000000000260ff60481b1991151568010000000000000000026001600160481b03199096166001600160401b03909516949094179490941793909316919091178255606084015184929182019062000a66908262000cdd565b506080820151805162000a8491600284019160209091019062000aaa565b5060a0820151805162000aa291600384019160209091019062000aaa565b505050505050565b82805482825590600052602060002090810192821562000af5579160200282015b8281111562000af5578251829062000ae4908262000cdd565b509160200191906001019062000acb565b5062000b0392915062000bb3565b5090565b82805482825590600052602060002090600f0160109004810192821562000ba55791602002820160005b8382111562000b7357835183826101000a81548161ffff021916908361ffff160217905550926020019260020160208160010104928301926001030262000b31565b801562000ba35782816101000a81549061ffff021916905560020160208160010104928301926001030262000b73565b505b5062000b0392915062000bd4565b8082111562000b0357600062000bca828262000beb565b5060010162000bb3565b5b8082111562000b03576000815560010162000bd5565b50805462000bf99062000c56565b6000825580601f1062000c0a575050565b601f016020900490600052602060002090810190620006c8919062000bd4565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600181811c9082168062000c6b57607f821691505b6020821081036200077b57634e487b7160e01b600052602260045260246000fd5b601f82111562000cd8576000816000526020600020601f850160051c8101602086101562000cb75750805b601f850160051c820191505b8181101562000aa25782815560010162000cc3565b505050565b81516001600160401b0381111562000cf95762000cf962000c2a565b62000d118162000d0a845462000c56565b8462000c8c565b602080601f83116001811462000d49576000841562000d305750858301515b600019600386901b1c1916600185901b17855562000aa2565b600085815260208120601f198616915b8281101562000d7a5788860151825594840194600190910190840162000d59565b508582101562000d995787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6129948062000db96000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c806389b0587411610097578063f0f52cea11610066578063f0f52cea1461021b578063f2fde38b1461022e578063f6ca742814610241578063f908e3131461025457600080fd5b806389b05874146101c55780638da5cb5b146101d8578063da839a8a146101e9578063e3a404751461020857600080fd5b80632f69201d116100d35780632f69201d14610177578063715018a61461018a57806383c73583146101925780638765dc20146101a557600080fd5b80630e63540a146100fa57806316100eb91461012b5780632405f95a14610140575b600080fd5b61010d610108366004611e1c565b610265565b60405167ffffffffffffffff90911681526020015b60405180910390f35b61013e610139366004611e46565b61030e565b005b61015f61014e366004611e1c565b50506007546001600160a01b031690565b6040516001600160a01b039091168152602001610122565b61013e610185366004612078565b610344565b61013e61047e565b61013e6101a0366004612078565b610492565b6101b86101b3366004612145565b6105be565b60405161012291906121ad565b61013e6101d3366004611e46565b6105d5565b6000546001600160a01b031661015f565b61015f6101f7366004611e1c565b50506008546001600160a01b031690565b61013e610216366004611e46565b6105e6565b6101b8610229366004612145565b6105f7565b61013e61023c366004611e46565b610605565b61013e61024f36600461222c565b610692565b6009546001600160a01b031661015f565b60006102796008546001600160a01b031690565b6040517f1d5bba7f0000000000000000000000000000000000000000000000000000000081526001600160a01b038581166004830152602482018590529190911690631d5bba7f90604401602060405180830381865afa1580156102e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103059190612331565b90505b92915050565b610316610751565b6009805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03831617905550565b50565b61034c610751565b83516002146103925760405162461bcd60e51b815260206004820152600d60248201526c496e76616c696420666c61677360981b60448201526064015b60405180910390fd5b805161039e6001610795565b6020015151146103f05760405162461bcd60e51b815260206004820152601260248201527f496e76616c6964206174747269627574657300000000000000000000000000006044820152606401610389565b60006040518060c001604052808767ffffffffffffffff1681526020018660008151811061042057610420612355565b6020026020010151151581526020018660018151811061044257610442612355565b6020026020010151151581526020018581526020018481526020018381525090506104758161046f600190565b90610a9a565b50505050505050565b610486610c54565b6104906000610cae565b565b61049a610751565b83516002146104db5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c696420666c61677360981b6044820152606401610389565b80516104e76001610795565b6020015151146105395760405162461bcd60e51b815260206004820152601260248201527f496e76616c6964206174747269627574657300000000000000000000000000006044820152606401610389565b60006040518060c001604052808767ffffffffffffffff1681526020018660008151811061056957610569612355565b6020026020010151151581526020018660018151811061058b5761058b612355565b602002602001015115158152602001858152602001848152602001838152509050610475816105b8600190565b90610d0b565b60606105cc85858585610e6c565b95945050505050565b6105dd610751565b61034181610e7a565b6105ee610751565b61034181610eb6565b60606105cc85858585610ef2565b61060d610c54565b6001600160a01b0381166106895760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610389565b61034181610cae565b61069a610751565b60006106a66001610795565b905088888080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506106eb868861236b565b60208201526106fa848661236b565b81604001819052508282808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050506060820152610745600182610f08565b50505050505050505050565b6000546001600160a01b03163314610490576040517f6db2465f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107c06040518060800160405280606081526020016060815260200160608152602001606081525090565b816001016040518060800160405290816000820180546107df90612378565b80601f016020809104026020016040519081016040528092919081815260200182805461080b90612378565b80156108585780601f1061082d57610100808354040283529160200191610858565b820191906000526020600020905b81548152906001019060200180831161083b57829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020016000905b828210156109325783829060005260206000200180546108a590612378565b80601f01602080910402602001604051908101604052809291908181526020018280546108d190612378565b801561091e5780601f106108f35761010080835404028352916020019161091e565b820191906000526020600020905b81548152906001019060200180831161090157829003601f168201915b505050505081526020019060010190610886565b50505050815260200160028201805480602002602001604051908101604052809291908181526020016000905b82821015610a0b57838290600052602060002001805461097e90612378565b80601f01602080910402602001604051908101604052809291908181526020018280546109aa90612378565b80156109f75780601f106109cc576101008083540402835291602001916109f7565b820191906000526020600020905b8154815290600101906020018083116109da57829003601f168201915b50505050508152602001906001019061095f565b50505050815260200160038201805480602002602001604051908101604052809291908181526020018280548015610a8a57602002820191906000526020600020906000905b82829054906101000a900461ffff1661ffff1681526020019060020190602082600101049283019260010382029150808411610a515790505b5050505050815250509050919050565b6001816000015167ffffffffffffffff161080610ac557506005820154815167ffffffffffffffff16115b15610ae35760405163d8a7da6b60e01b815260040160405180910390fd5b80516005830190610af6906001906123c8565b67ffffffffffffffff1681548110610b1057610b10612355565b6000918252602082206004909102015467ffffffffffffffff169003610b62576040517f2e0805ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808260050160018360000151610b7891906123c8565b67ffffffffffffffff1681548110610b9257610b92612355565b600091825260209182902083516004909202018054928401516040850151151569010000000000000000000269ff00000000000000000019911515680100000000000000000268ffffffffffffffffff1990951667ffffffffffffffff90941693909317939093179290921617815560608201516001820190610c15908261243d565b5060808201518051610c31916002840191602090910190611c99565b5060a08201518051610c4d916003840191602090910190611c99565b5050505050565b6000546001600160a01b031633146104905760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610389565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805167ffffffffffffffff8116600003610d51576040517fcc4ccb6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600583015467ffffffffffffffff821611610d98576040517f858c1d2900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058301805460018181018355600092835260209283902085516004909302018054938601516040870151151569010000000000000000000269ff00000000000000000019911515680100000000000000000268ffffffffffffffffff1990961667ffffffffffffffff9095169490941794909417939093169190911782556060840151849291820190610e2c908261243d565b5060808201518051610e48916002840191602090910190611c99565b5060a08201518051610e64916003840191602090910190611c99565b505050505050565b60606105cc85858585610f6e565b6001600160a01b0381161561034157600880546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b6001600160a01b0381161561034157600780546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b60606105cc610f0386868686610f6e565b611136565b8051819060018401908190610f1d908261243d565b506020828101518051610f369260018501920190611c99565b5060408201518051610f52916002840191602090910190611c99565b5060608201518051610c4d916003840191602090910190611cef565b60606000610f91610f89610f848660101c151590565b611167565b600190611178565b805190915067ffffffffffffffff16600003610fe5576040517fbbf5531700000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86166004820152602401610389565b6000610ff16001610795565b9050816060015182602001516110165760405180602001604052806000815250611023565b6110238661ffff166114c4565b61103483858a8a61ffff168a611564565b604051602001611046939291906124fd565b604051602081830303815290604052925082611063888888611687565b836040015161108157604051806020016040528060008152506110b8565b6040518060400160405280601381526020017f222c22616e696d6174696f6e5f75726c223a22000000000000000000000000008152505b84604001516110d657604051806020016040528060008152506110e1565b6110e18a8a8961173c565b61110485876110f5610f848d60101c151590565b8c61ffff1660108e901c6117a8565b855160405161111b96959493929190602001612597565b60405160208183030381529060405292505050949350505050565b606061114182611989565b60405160200161115191906126a8565b6040516020818303038152906040529050919050565b6000610308821515600181176126ed565b6040805160c0810182526000808252602082018190529181019190915260608082018190526080820181905260a082015260018267ffffffffffffffff1610806111cf5750600583015467ffffffffffffffff8316115b156111ed5760405163d8a7da6b60e01b815260040160405180910390fd5b600583016111fc6001846123c8565b67ffffffffffffffff168154811061121657611216612355565b60009182526020918290206040805160c0810182526004909302909101805467ffffffffffffffff8116845260ff68010000000000000000820481161515958501959095526901000000000000000000900490931615159082015260018201805491929160608401919061128990612378565b80601f01602080910402602001604051908101604052809291908181526020018280546112b590612378565b80156113025780601f106112d757610100808354040283529160200191611302565b820191906000526020600020905b8154815290600101906020018083116112e557829003601f168201915b5050505050815260200160028201805480602002602001604051908101604052809291908181526020016000905b828210156113dc57838290600052602060002001805461134f90612378565b80601f016020809104026020016040519081016040528092919081815260200182805461137b90612378565b80156113c85780601f1061139d576101008083540402835291602001916113c8565b820191906000526020600020905b8154815290600101906020018083116113ab57829003601f168201915b505050505081526020019060010190611330565b50505050815260200160038201805480602002602001604051908101604052809291908181526020016000905b828210156114b557838290600052602060002001805461142890612378565b80601f016020809104026020016040519081016040528092919081815260200182805461145490612378565b80156114a15780601f10611476576101008083540402835291602001916114a1565b820191906000526020600020905b81548152906001019060200180831161148457829003601f168201915b505050505081526020019060010190611409565b50505091525090949350505050565b606060006114d183611ae9565b600101905060008167ffffffffffffffff8111156114f1576114f1611e82565b6040519080825280601f01601f19166020018201604052801561151b576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461152557509392505050565b60606000805b87604001515182101561167c578760400151828151811061158d5761158d612355565b60200260200101515160000361162f57808860600151511180156115d1575081886060015182815181106115c3576115c3612355565b602002602001015161ffff16145b1561162a5782876080015182815181106115ed576115ed612355565b602002602001015160405160200161160692919061270e565b604051602081830303815290604052925080806116229061273d565b915050611671565b611671565b828860400151838151811061164657611646612355565b602002602001015160405160200161165f92919061270e565b60405160208183030381529060405292505b60019091019061156a565b505095945050505050565b606061169b6007546001600160a01b031690565b6040517f1e5dcf0f00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff85166004820152602481018490526001600160a01b039190911690631e5dcf0f906044015b600060405180830381865afa15801561170c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526117349190810190612756565b949350505050565b60606117506007546001600160a01b031690565b6040517f523786fd00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff85166004820152602481018490526001600160a01b03919091169063523786fd906044016116ef565b6040805180820190915260018082527f5b00000000000000000000000000000000000000000000000000000000000000602083015260009067ffffffffffffffff861690036118a0575b600181101561189b5781811561182157604051806040016040528060018152602001600b60fa1b815250611832565b604051806020016040528060008152505b8860200151838151811061184857611848612355565b60200260200101518860a00151848151811061186657611866612355565b602002602001015160405160200161188194939291906127c4565b60408051601f1981840301815291905291506001016117f2565b61195d565b8467ffffffffffffffff1660020361195d575060015b86602001515181101561195d5781600182146118eb57604051806040016040528060018152602001600b60fa1b8152506118fc565b604051806020016040528060008152505b8860200151838151811061191257611912612355565b602002602001015161193060018561192a919061287e565b87611bcb565b60405160200161194394939291906127c4565b60408051601f1981840301815291905291506001016118b6565b8160405160200161196e9190612891565b60405160208183030381529060405291505095945050505050565b606081516000036119a857505060408051602081019091526000815290565b600060405180606001604052806040815260200161291f60409139905060006003845160026119d791906128d2565b6119e191906128e5565b6119ec906004612907565b67ffffffffffffffff811115611a0457611a04611e82565b6040519080825280601f01601f191660200182016040528015611a2e576020820181803683370190505b50905060018201602082018586518701602081018051600082525b82841015611aa4576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450611a49565b9052505085516003900660018114611ac35760028114611ad657611ade565b603d6001830353603d6002830353611ade565b603d60018303535b509195945050505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310611b32577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310611b5e576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310611b7c57662386f26fc10000830492506010015b6305f5e1008310611b94576305f5e100830492506008015b6127108310611ba857612710830492506004015b60648310611bba576064830492506002015b600a83106103085760010192915050565b6060611bdf6009546001600160a01b031690565b6001600160a01b031663b46c1d4584611bf88587611c7c565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b168152600481019290925260ff166024820152604401600060405180830381865afa158015611c54573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103059190810190612756565b600080611c8a836008612907565b84901c60ff1691505092915050565b828054828255906000526020600020908101928215611cdf579160200282015b82811115611cdf5782518290611ccf908261243d565b5091602001919060010190611cb9565b50611ceb929150611d94565b5090565b82805482825590600052602060002090600f01601090048101928215611d885791602002820160005b83821115611d5857835183826101000a81548161ffff021916908361ffff1602179055509260200192600201602081600101049283019260010302611d18565b8015611d865782816101000a81549061ffff0219169055600201602081600101049283019260010302611d58565b505b50611ceb929150611db1565b80821115611ceb576000611da88282611dc6565b50600101611d94565b5b80821115611ceb5760008155600101611db2565b508054611dd290612378565b6000825580601f10611de2575050565b601f0160209004906000526020600020908101906103419190611db1565b80356001600160a01b0381168114611e1757600080fd5b919050565b60008060408385031215611e2f57600080fd5b611e3883611e00565b946020939093013593505050565b600060208284031215611e5857600080fd5b61030582611e00565b67ffffffffffffffff8116811461034157600080fd5b8035611e1781611e61565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611ec157611ec1611e82565b604052919050565b600067ffffffffffffffff821115611ee357611ee3611e82565b5060051b60200190565b600082601f830112611efe57600080fd5b81356020611f13611f0e83611ec9565b611e98565b8083825260208201915060208460051b870101935086841115611f3557600080fd5b602086015b84811015611f605780358015158114611f535760008081fd5b8352918301918301611f3a565b509695505050505050565b600067ffffffffffffffff821115611f8557611f85611e82565b50601f01601f191660200190565b600082601f830112611fa457600080fd5b8135611fb2611f0e82611f6b565b818152846020838601011115611fc757600080fd5b816020850160208301376000918101602001919091529392505050565b6000611ff2611f0e84611ec9565b8381529050602080820190600585901b84018681111561201157600080fd5b845b8181101561204d57803567ffffffffffffffff8111156120335760008081fd5b61203f89828901611f93565b855250928201928201612013565b505050509392505050565b600082601f83011261206957600080fd5b61030583833560208501611fe4565b60008060008060008060c0878903121561209157600080fd5b61209a87611e00565b95506120a860208801611e77565b9450604087013567ffffffffffffffff808211156120c557600080fd5b6120d18a838b01611eed565b955060608901359150808211156120e757600080fd5b6120f38a838b01611f93565b9450608089013591508082111561210957600080fd5b6121158a838b01612058565b935060a089013591508082111561212b57600080fd5b5061213889828a01612058565b9150509295509295509295565b6000806000806080858703121561215b57600080fd5b61216485611e00565b9350602085013561217481611e61565b93969395505050506040820135916060013590565b60005b838110156121a457818101518382015260200161218c565b50506000910152565b60208152600082518060208401526121cc816040850160208701612189565b601f01601f19169190910160400192915050565b60008083601f8401126121f257600080fd5b50813567ffffffffffffffff81111561220a57600080fd5b6020830191508360208260051b850101111561222557600080fd5b9250929050565b600080600080600080600080600060a08a8c03121561224a57600080fd5b6122538a611e00565b985060208a013567ffffffffffffffff8082111561227057600080fd5b818c0191508c601f83011261228457600080fd5b81358181111561229357600080fd5b8d60208285010111156122a557600080fd5b602083019a508099505060408c01359150808211156122c357600080fd5b6122cf8d838e016121e0565b909850965060608c01359150808211156122e857600080fd5b6122f48d838e016121e0565b909650945060808c013591508082111561230d57600080fd5b5061231a8c828d016121e0565b915080935050809150509295985092959850929598565b60006020828403121561234357600080fd5b815161234e81611e61565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000610305368484611fe4565b600181811c9082168061238c57607f821691505b6020821081036123ac57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b67ffffffffffffffff8281168282160390808211156123e9576123e96123b2565b5092915050565b601f821115612438576000816000526020600020601f850160051c810160208610156124195750805b601f850160051c820191505b81811015610e6457828155600101612425565b505050565b815167ffffffffffffffff81111561245757612457611e82565b61246b816124658454612378565b846123f0565b602080601f8311600181146124a057600084156124885750858301515b600019600386901b1c1916600185901b178555610e64565b600085815260208120601f198616915b828110156124cf578886015182559484019460019091019084016124b0565b50858210156124ed5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b7f7b226e616d65223a220000000000000000000000000000000000000000000000815260008451612535816009850160208901612189565b84519083019061254c816009840160208901612189565b7f222c226465736372697074696f6e223a2200000000000000000000000000000060099290910191820152835161258a81601a840160208801612189565b01601a0195945050505050565b6000875160206125aa8285838d01612189565b7f222c22696d616765223a2200000000000000000000000000000000000000000091840191825288516125e381600b8501848d01612189565b88519201916125f881600b8501848c01612189565b875192019161260d81600b8501848b01612189565b7f222c2261747472696275746573223a0000000000000000000000000000000000600b9390910192830152855161264a81601a8501848a01612189565b7f2c2265787465726e616c5f75726c223a22000000000000000000000000000000601a9390910192830152845161268781602b8501848901612189565b61227d60f01b9201908101600b0191909152602d0198975050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516126e081601d850160208701612189565b91909101601d0192915050565b67ffffffffffffffff8181168382160190808211156123e9576123e96123b2565b60008351612720818460208801612189565b835190830190612734818360208801612189565b01949350505050565b60006001820161274f5761274f6123b2565b5060010190565b60006020828403121561276857600080fd5b815167ffffffffffffffff81111561277f57600080fd5b8201601f8101841361279057600080fd5b805161279e611f0e82611f6b565b8181528560208385010111156127b357600080fd5b6105cc826020830160208601612189565b600085516127d6818460208a01612189565b8551908301906127ea818360208a01612189565b7f7b2274726169745f74797065223a2200000000000000000000000000000000009101908152845161282381600f840160208901612189565b7f222c2276616c7565223a22000000000000000000000000000000000000000000600f9290910191820152835161286181601a840160208801612189565b61227d60f01b601a9290910191820152601c019695505050505050565b81810381811115610308576103086123b2565b600082516128a3818460208701612189565b7f5d00000000000000000000000000000000000000000000000000000000000000920191825250600101919050565b80820180821115610308576103086123b2565b60008261290257634e487b7160e01b600052601260045260246000fd5b500490565b8082028115828204841417610308576103086123b256fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa264697066735822122064abda16270bbcad146fb9a43c3afa35e3f387693efb468fddfcbfc3dac141bc64736f6c63430008160033205468697320746f6b656e20697320707265736572766564206f6e2074686520457468657265756d20626c6f636b636861696e2e556e69717565206f75747075742066726f6d205f534f55524345205b4f6e204e4654735d5f2c20636f6d6d697373696f6e6564206279205441534348454e2c20616e642072656c656173656420627920436872697374696573206173207468652067656e657261746976652061727469737469632070726f6a65637420746f20636f6d65206f7574206f66205f4f6e204e4654735f2c20746865206c6172676573742061727420686973746f726963616c207374756479206f6e204e46547320746f20646174652e205265666572656e63696e672074686520696d706f7274616e6365206f6620626c6f636b636861696e732061732061206e657720666f726d206f66207075626c697368696e672c20616e64207468652069646561206f66204e4654732061732066756e64616d656e74616c6c79206d616465206f7574206f6620746578742c2074686520776f726b7320706f65746963616c6c792064697374696c6c20746865207072652d686973746f7279206f66204e46547320696e746f206c6172676520636f6e74656d706f72617279206469676974616c20636f6c6f72206669656c6473207061696e74696e67732c206d6564696174656420766961206d616368696e65206c6561726e696e6720616e64204e4c502e20506c6179696e67207769746820746865206e6f74696f6e206f66206469676974616c2067726166666974692c2074686520776f726b732072656d69782074686520636f6c6f72206669656c6420666f726d617420617320612063656c6562726174696f6e206f662052474220616e64206f66204e465473206173207065726d616e656e74206d61726b206d616b696e67206f6e20636861696e2e2068747470733a2f2f6e66742e6368726973746965732e636f6d2f66656174757265732f736f75726365205468697320746f6b656e206973207374696c6c206265696e6720707265736572766564206f6e2074686520626c6f636b636861696e2e2e2e204558504c4f5245204d4f5245204154205b524f42455254414c4943452e434f4d5d2868747470733a2f2f726f62657274616c6963652e636f6d2920

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100f55760003560e01c806389b0587411610097578063f0f52cea11610066578063f0f52cea1461021b578063f2fde38b1461022e578063f6ca742814610241578063f908e3131461025457600080fd5b806389b05874146101c55780638da5cb5b146101d8578063da839a8a146101e9578063e3a404751461020857600080fd5b80632f69201d116100d35780632f69201d14610177578063715018a61461018a57806383c73583146101925780638765dc20146101a557600080fd5b80630e63540a146100fa57806316100eb91461012b5780632405f95a14610140575b600080fd5b61010d610108366004611e1c565b610265565b60405167ffffffffffffffff90911681526020015b60405180910390f35b61013e610139366004611e46565b61030e565b005b61015f61014e366004611e1c565b50506007546001600160a01b031690565b6040516001600160a01b039091168152602001610122565b61013e610185366004612078565b610344565b61013e61047e565b61013e6101a0366004612078565b610492565b6101b86101b3366004612145565b6105be565b60405161012291906121ad565b61013e6101d3366004611e46565b6105d5565b6000546001600160a01b031661015f565b61015f6101f7366004611e1c565b50506008546001600160a01b031690565b61013e610216366004611e46565b6105e6565b6101b8610229366004612145565b6105f7565b61013e61023c366004611e46565b610605565b61013e61024f36600461222c565b610692565b6009546001600160a01b031661015f565b60006102796008546001600160a01b031690565b6040517f1d5bba7f0000000000000000000000000000000000000000000000000000000081526001600160a01b038581166004830152602482018590529190911690631d5bba7f90604401602060405180830381865afa1580156102e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103059190612331565b90505b92915050565b610316610751565b6009805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03831617905550565b50565b61034c610751565b83516002146103925760405162461bcd60e51b815260206004820152600d60248201526c496e76616c696420666c61677360981b60448201526064015b60405180910390fd5b805161039e6001610795565b6020015151146103f05760405162461bcd60e51b815260206004820152601260248201527f496e76616c6964206174747269627574657300000000000000000000000000006044820152606401610389565b60006040518060c001604052808767ffffffffffffffff1681526020018660008151811061042057610420612355565b6020026020010151151581526020018660018151811061044257610442612355565b6020026020010151151581526020018581526020018481526020018381525090506104758161046f600190565b90610a9a565b50505050505050565b610486610c54565b6104906000610cae565b565b61049a610751565b83516002146104db5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c696420666c61677360981b6044820152606401610389565b80516104e76001610795565b6020015151146105395760405162461bcd60e51b815260206004820152601260248201527f496e76616c6964206174747269627574657300000000000000000000000000006044820152606401610389565b60006040518060c001604052808767ffffffffffffffff1681526020018660008151811061056957610569612355565b6020026020010151151581526020018660018151811061058b5761058b612355565b602002602001015115158152602001858152602001848152602001838152509050610475816105b8600190565b90610d0b565b60606105cc85858585610e6c565b95945050505050565b6105dd610751565b61034181610e7a565b6105ee610751565b61034181610eb6565b60606105cc85858585610ef2565b61060d610c54565b6001600160a01b0381166106895760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610389565b61034181610cae565b61069a610751565b60006106a66001610795565b905088888080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506106eb868861236b565b60208201526106fa848661236b565b81604001819052508282808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050506060820152610745600182610f08565b50505050505050505050565b6000546001600160a01b03163314610490576040517f6db2465f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107c06040518060800160405280606081526020016060815260200160608152602001606081525090565b816001016040518060800160405290816000820180546107df90612378565b80601f016020809104026020016040519081016040528092919081815260200182805461080b90612378565b80156108585780601f1061082d57610100808354040283529160200191610858565b820191906000526020600020905b81548152906001019060200180831161083b57829003601f168201915b5050505050815260200160018201805480602002602001604051908101604052809291908181526020016000905b828210156109325783829060005260206000200180546108a590612378565b80601f01602080910402602001604051908101604052809291908181526020018280546108d190612378565b801561091e5780601f106108f35761010080835404028352916020019161091e565b820191906000526020600020905b81548152906001019060200180831161090157829003601f168201915b505050505081526020019060010190610886565b50505050815260200160028201805480602002602001604051908101604052809291908181526020016000905b82821015610a0b57838290600052602060002001805461097e90612378565b80601f01602080910402602001604051908101604052809291908181526020018280546109aa90612378565b80156109f75780601f106109cc576101008083540402835291602001916109f7565b820191906000526020600020905b8154815290600101906020018083116109da57829003601f168201915b50505050508152602001906001019061095f565b50505050815260200160038201805480602002602001604051908101604052809291908181526020018280548015610a8a57602002820191906000526020600020906000905b82829054906101000a900461ffff1661ffff1681526020019060020190602082600101049283019260010382029150808411610a515790505b5050505050815250509050919050565b6001816000015167ffffffffffffffff161080610ac557506005820154815167ffffffffffffffff16115b15610ae35760405163d8a7da6b60e01b815260040160405180910390fd5b80516005830190610af6906001906123c8565b67ffffffffffffffff1681548110610b1057610b10612355565b6000918252602082206004909102015467ffffffffffffffff169003610b62576040517f2e0805ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808260050160018360000151610b7891906123c8565b67ffffffffffffffff1681548110610b9257610b92612355565b600091825260209182902083516004909202018054928401516040850151151569010000000000000000000269ff00000000000000000019911515680100000000000000000268ffffffffffffffffff1990951667ffffffffffffffff90941693909317939093179290921617815560608201516001820190610c15908261243d565b5060808201518051610c31916002840191602090910190611c99565b5060a08201518051610c4d916003840191602090910190611c99565b5050505050565b6000546001600160a01b031633146104905760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610389565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b805167ffffffffffffffff8116600003610d51576040517fcc4ccb6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600583015467ffffffffffffffff821611610d98576040517f858c1d2900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60058301805460018181018355600092835260209283902085516004909302018054938601516040870151151569010000000000000000000269ff00000000000000000019911515680100000000000000000268ffffffffffffffffff1990961667ffffffffffffffff9095169490941794909417939093169190911782556060840151849291820190610e2c908261243d565b5060808201518051610e48916002840191602090910190611c99565b5060a08201518051610e64916003840191602090910190611c99565b505050505050565b60606105cc85858585610f6e565b6001600160a01b0381161561034157600880546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b6001600160a01b0381161561034157600780546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b60606105cc610f0386868686610f6e565b611136565b8051819060018401908190610f1d908261243d565b506020828101518051610f369260018501920190611c99565b5060408201518051610f52916002840191602090910190611c99565b5060608201518051610c4d916003840191602090910190611cef565b60606000610f91610f89610f848660101c151590565b611167565b600190611178565b805190915067ffffffffffffffff16600003610fe5576040517fbbf5531700000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86166004820152602401610389565b6000610ff16001610795565b9050816060015182602001516110165760405180602001604052806000815250611023565b6110238661ffff166114c4565b61103483858a8a61ffff168a611564565b604051602001611046939291906124fd565b604051602081830303815290604052925082611063888888611687565b836040015161108157604051806020016040528060008152506110b8565b6040518060400160405280601381526020017f222c22616e696d6174696f6e5f75726c223a22000000000000000000000000008152505b84604001516110d657604051806020016040528060008152506110e1565b6110e18a8a8961173c565b61110485876110f5610f848d60101c151590565b8c61ffff1660108e901c6117a8565b855160405161111b96959493929190602001612597565b60405160208183030381529060405292505050949350505050565b606061114182611989565b60405160200161115191906126a8565b6040516020818303038152906040529050919050565b6000610308821515600181176126ed565b6040805160c0810182526000808252602082018190529181019190915260608082018190526080820181905260a082015260018267ffffffffffffffff1610806111cf5750600583015467ffffffffffffffff8316115b156111ed5760405163d8a7da6b60e01b815260040160405180910390fd5b600583016111fc6001846123c8565b67ffffffffffffffff168154811061121657611216612355565b60009182526020918290206040805160c0810182526004909302909101805467ffffffffffffffff8116845260ff68010000000000000000820481161515958501959095526901000000000000000000900490931615159082015260018201805491929160608401919061128990612378565b80601f01602080910402602001604051908101604052809291908181526020018280546112b590612378565b80156113025780601f106112d757610100808354040283529160200191611302565b820191906000526020600020905b8154815290600101906020018083116112e557829003601f168201915b5050505050815260200160028201805480602002602001604051908101604052809291908181526020016000905b828210156113dc57838290600052602060002001805461134f90612378565b80601f016020809104026020016040519081016040528092919081815260200182805461137b90612378565b80156113c85780601f1061139d576101008083540402835291602001916113c8565b820191906000526020600020905b8154815290600101906020018083116113ab57829003601f168201915b505050505081526020019060010190611330565b50505050815260200160038201805480602002602001604051908101604052809291908181526020016000905b828210156114b557838290600052602060002001805461142890612378565b80601f016020809104026020016040519081016040528092919081815260200182805461145490612378565b80156114a15780601f10611476576101008083540402835291602001916114a1565b820191906000526020600020905b81548152906001019060200180831161148457829003601f168201915b505050505081526020019060010190611409565b50505091525090949350505050565b606060006114d183611ae9565b600101905060008167ffffffffffffffff8111156114f1576114f1611e82565b6040519080825280601f01601f19166020018201604052801561151b576020820181803683370190505b5090508181016020015b600019017f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a850494508461152557509392505050565b60606000805b87604001515182101561167c578760400151828151811061158d5761158d612355565b60200260200101515160000361162f57808860600151511180156115d1575081886060015182815181106115c3576115c3612355565b602002602001015161ffff16145b1561162a5782876080015182815181106115ed576115ed612355565b602002602001015160405160200161160692919061270e565b604051602081830303815290604052925080806116229061273d565b915050611671565b611671565b828860400151838151811061164657611646612355565b602002602001015160405160200161165f92919061270e565b60405160208183030381529060405292505b60019091019061156a565b505095945050505050565b606061169b6007546001600160a01b031690565b6040517f1e5dcf0f00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff85166004820152602481018490526001600160a01b039190911690631e5dcf0f906044015b600060405180830381865afa15801561170c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526117349190810190612756565b949350505050565b60606117506007546001600160a01b031690565b6040517f523786fd00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff85166004820152602481018490526001600160a01b03919091169063523786fd906044016116ef565b6040805180820190915260018082527f5b00000000000000000000000000000000000000000000000000000000000000602083015260009067ffffffffffffffff861690036118a0575b600181101561189b5781811561182157604051806040016040528060018152602001600b60fa1b815250611832565b604051806020016040528060008152505b8860200151838151811061184857611848612355565b60200260200101518860a00151848151811061186657611866612355565b602002602001015160405160200161188194939291906127c4565b60408051601f1981840301815291905291506001016117f2565b61195d565b8467ffffffffffffffff1660020361195d575060015b86602001515181101561195d5781600182146118eb57604051806040016040528060018152602001600b60fa1b8152506118fc565b604051806020016040528060008152505b8860200151838151811061191257611912612355565b602002602001015161193060018561192a919061287e565b87611bcb565b60405160200161194394939291906127c4565b60408051601f1981840301815291905291506001016118b6565b8160405160200161196e9190612891565b60405160208183030381529060405291505095945050505050565b606081516000036119a857505060408051602081019091526000815290565b600060405180606001604052806040815260200161291f60409139905060006003845160026119d791906128d2565b6119e191906128e5565b6119ec906004612907565b67ffffffffffffffff811115611a0457611a04611e82565b6040519080825280601f01601f191660200182016040528015611a2e576020820181803683370190505b50905060018201602082018586518701602081018051600082525b82841015611aa4576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450611a49565b9052505085516003900660018114611ac35760028114611ad657611ade565b603d6001830353603d6002830353611ade565b603d60018303535b509195945050505050565b6000807a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310611b32577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000830492506040015b6d04ee2d6d415b85acef81000000008310611b5e576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc100008310611b7c57662386f26fc10000830492506010015b6305f5e1008310611b94576305f5e100830492506008015b6127108310611ba857612710830492506004015b60648310611bba576064830492506002015b600a83106103085760010192915050565b6060611bdf6009546001600160a01b031690565b6001600160a01b031663b46c1d4584611bf88587611c7c565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b168152600481019290925260ff166024820152604401600060405180830381865afa158015611c54573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103059190810190612756565b600080611c8a836008612907565b84901c60ff1691505092915050565b828054828255906000526020600020908101928215611cdf579160200282015b82811115611cdf5782518290611ccf908261243d565b5091602001919060010190611cb9565b50611ceb929150611d94565b5090565b82805482825590600052602060002090600f01601090048101928215611d885791602002820160005b83821115611d5857835183826101000a81548161ffff021916908361ffff1602179055509260200192600201602081600101049283019260010302611d18565b8015611d865782816101000a81549061ffff0219169055600201602081600101049283019260010302611d58565b505b50611ceb929150611db1565b80821115611ceb576000611da88282611dc6565b50600101611d94565b5b80821115611ceb5760008155600101611db2565b508054611dd290612378565b6000825580601f10611de2575050565b601f0160209004906000526020600020908101906103419190611db1565b80356001600160a01b0381168114611e1757600080fd5b919050565b60008060408385031215611e2f57600080fd5b611e3883611e00565b946020939093013593505050565b600060208284031215611e5857600080fd5b61030582611e00565b67ffffffffffffffff8116811461034157600080fd5b8035611e1781611e61565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611ec157611ec1611e82565b604052919050565b600067ffffffffffffffff821115611ee357611ee3611e82565b5060051b60200190565b600082601f830112611efe57600080fd5b81356020611f13611f0e83611ec9565b611e98565b8083825260208201915060208460051b870101935086841115611f3557600080fd5b602086015b84811015611f605780358015158114611f535760008081fd5b8352918301918301611f3a565b509695505050505050565b600067ffffffffffffffff821115611f8557611f85611e82565b50601f01601f191660200190565b600082601f830112611fa457600080fd5b8135611fb2611f0e82611f6b565b818152846020838601011115611fc757600080fd5b816020850160208301376000918101602001919091529392505050565b6000611ff2611f0e84611ec9565b8381529050602080820190600585901b84018681111561201157600080fd5b845b8181101561204d57803567ffffffffffffffff8111156120335760008081fd5b61203f89828901611f93565b855250928201928201612013565b505050509392505050565b600082601f83011261206957600080fd5b61030583833560208501611fe4565b60008060008060008060c0878903121561209157600080fd5b61209a87611e00565b95506120a860208801611e77565b9450604087013567ffffffffffffffff808211156120c557600080fd5b6120d18a838b01611eed565b955060608901359150808211156120e757600080fd5b6120f38a838b01611f93565b9450608089013591508082111561210957600080fd5b6121158a838b01612058565b935060a089013591508082111561212b57600080fd5b5061213889828a01612058565b9150509295509295509295565b6000806000806080858703121561215b57600080fd5b61216485611e00565b9350602085013561217481611e61565b93969395505050506040820135916060013590565b60005b838110156121a457818101518382015260200161218c565b50506000910152565b60208152600082518060208401526121cc816040850160208701612189565b601f01601f19169190910160400192915050565b60008083601f8401126121f257600080fd5b50813567ffffffffffffffff81111561220a57600080fd5b6020830191508360208260051b850101111561222557600080fd5b9250929050565b600080600080600080600080600060a08a8c03121561224a57600080fd5b6122538a611e00565b985060208a013567ffffffffffffffff8082111561227057600080fd5b818c0191508c601f83011261228457600080fd5b81358181111561229357600080fd5b8d60208285010111156122a557600080fd5b602083019a508099505060408c01359150808211156122c357600080fd5b6122cf8d838e016121e0565b909850965060608c01359150808211156122e857600080fd5b6122f48d838e016121e0565b909650945060808c013591508082111561230d57600080fd5b5061231a8c828d016121e0565b915080935050809150509295985092959850929598565b60006020828403121561234357600080fd5b815161234e81611e61565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000610305368484611fe4565b600181811c9082168061238c57607f821691505b6020821081036123ac57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b67ffffffffffffffff8281168282160390808211156123e9576123e96123b2565b5092915050565b601f821115612438576000816000526020600020601f850160051c810160208610156124195750805b601f850160051c820191505b81811015610e6457828155600101612425565b505050565b815167ffffffffffffffff81111561245757612457611e82565b61246b816124658454612378565b846123f0565b602080601f8311600181146124a057600084156124885750858301515b600019600386901b1c1916600185901b178555610e64565b600085815260208120601f198616915b828110156124cf578886015182559484019460019091019084016124b0565b50858210156124ed5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b7f7b226e616d65223a220000000000000000000000000000000000000000000000815260008451612535816009850160208901612189565b84519083019061254c816009840160208901612189565b7f222c226465736372697074696f6e223a2200000000000000000000000000000060099290910191820152835161258a81601a840160208801612189565b01601a0195945050505050565b6000875160206125aa8285838d01612189565b7f222c22696d616765223a2200000000000000000000000000000000000000000091840191825288516125e381600b8501848d01612189565b88519201916125f881600b8501848c01612189565b875192019161260d81600b8501848b01612189565b7f222c2261747472696275746573223a0000000000000000000000000000000000600b9390910192830152855161264a81601a8501848a01612189565b7f2c2265787465726e616c5f75726c223a22000000000000000000000000000000601a9390910192830152845161268781602b8501848901612189565b61227d60f01b9201908101600b0191909152602d0198975050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c0000008152600082516126e081601d850160208701612189565b91909101601d0192915050565b67ffffffffffffffff8181168382160190808211156123e9576123e96123b2565b60008351612720818460208801612189565b835190830190612734818360208801612189565b01949350505050565b60006001820161274f5761274f6123b2565b5060010190565b60006020828403121561276857600080fd5b815167ffffffffffffffff81111561277f57600080fd5b8201601f8101841361279057600080fd5b805161279e611f0e82611f6b565b8181528560208385010111156127b357600080fd5b6105cc826020830160208601612189565b600085516127d6818460208a01612189565b8551908301906127ea818360208a01612189565b7f7b2274726169745f74797065223a2200000000000000000000000000000000009101908152845161282381600f840160208901612189565b7f222c2276616c7565223a22000000000000000000000000000000000000000000600f9290910191820152835161286181601a840160208801612189565b61227d60f01b601a9290910191820152601c019695505050505050565b81810381811115610308576103086123b2565b600082516128a3818460208701612189565b7f5d00000000000000000000000000000000000000000000000000000000000000920191825250600101919050565b80820180821115610308576103086123b2565b60008261290257634e487b7160e01b600052601260045260246000fd5b500490565b8082028115828204841417610308576103086123b256fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa264697066735822122064abda16270bbcad146fb9a43c3afa35e3f387693efb468fddfcbfc3dac141bc64736f6c63430008160033

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

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.