ETH Price: $3,086.82 (+0.82%)
Gas: 5 Gwei

Contract

0xa2ccfE293bc2CDD78D8166a82D1e18cD2148122b
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Set Minter For P...202684102024-07-09 10:42:2322 hrs ago1720521743IN
0xa2ccfE29...D2148122b
0 ETH0.000590724.22573948
Set Minter For P...202611132024-07-08 10:11:3546 hrs ago1720433495IN
0xa2ccfE29...D2148122b
0 ETH0.000444553.06443406
Set Minter For P...202220152024-07-02 23:09:597 days ago1719961799IN
0xa2ccfE29...D2148122b
0 ETH0.000467273.54987071
Set Minter For P...201474372024-06-22 13:06:5917 days ago1719061619IN
0xa2ccfE29...D2148122b
0 ETH0.000606674.07835749
Set Minter For P...201462832024-06-22 9:14:3517 days ago1719047675IN
0xa2ccfE29...D2148122b
0 ETH0.000291462.21910024
Set Minter For P...201381862024-06-21 6:04:5919 days ago1718949899IN
0xa2ccfE29...D2148122b
0 ETH0.000181942.65583692
Set Minter For P...201341452024-06-20 16:31:1119 days ago1718901071IN
0xa2ccfE29...D2148122b
0 ETH0.0018861214.75047353
Set Minter For P...200688272024-06-11 13:17:1128 days ago1718111831IN
0xa2ccfE29...D2148122b
0 ETH0.0011518516.85552012
Set Minter For P...200412862024-06-07 16:56:5932 days ago1717779419IN
0xa2ccfE29...D2148122b
0 ETH0.0014003918.7464255
Set Minter For P...200344812024-06-06 18:09:3533 days ago1717697375IN
0xa2ccfE29...D2148122b
0 ETH0.0026536535.44154887
Set Minter For P...200333272024-06-06 14:17:2333 days ago1717683443IN
0xa2ccfE29...D2148122b
0 ETH0.0018760529.44726744
Set Minter For P...199894242024-05-31 11:09:4739 days ago1717153787IN
0xa2ccfE29...D2148122b
0 ETH0.001067438.11600449
Set Minter For P...199886272024-05-31 8:29:1140 days ago1717144151IN
0xa2ccfE29...D2148122b
0 ETH0.000760315.78086904
Set Minter For P...199885782024-05-31 8:19:2340 days ago1717143563IN
0xa2ccfE29...D2148122b
0 ETH0.000990056.65957367
Set Minter For P...199341332024-05-23 17:40:4747 days ago1716486047IN
0xa2ccfE29...D2148122b
0 ETH0.0009102513.28009556
Set Minter For P...199341152024-05-23 17:37:1147 days ago1716485831IN
0xa2ccfE29...D2148122b
0 ETH0.0009806114.34719252
Set Minter For P...199279912024-05-22 21:05:3548 days ago1716411935IN
0xa2ccfE29...D2148122b
0 ETH0.0021530514.82175637
Set Minter For P...199091232024-05-20 5:42:4751 days ago1716183767IN
0xa2ccfE29...D2148122b
0 ETH0.000377232.86820862
Set Minter For P...199071312024-05-19 23:02:1151 days ago1716159731IN
0xa2ccfE29...D2148122b
0 ETH0.000336882.56141261
Set Minter For P...198368462024-05-10 3:06:2361 days ago1715310383IN
0xa2ccfE29...D2148122b
0 ETH0.000207043.02067348
Set Minter For P...198367762024-05-10 2:52:2361 days ago1715309543IN
0xa2ccfE29...D2148122b
0 ETH0.000242933.5544033
Set Minter For P...198054532024-05-05 17:42:5965 days ago1714930979IN
0xa2ccfE29...D2148122b
0 ETH0.000538987.86339619
Set Minter For P...197621912024-04-29 16:34:4771 days ago1714408487IN
0xa2ccfE29...D2148122b
0 ETH0.0009988211.6894273
Set Minter For P...197621022024-04-29 16:16:5971 days ago1714407419IN
0xa2ccfE29...D2148122b
0 ETH0.0009621715.14351106
Set Minter For P...197420072024-04-26 20:49:1174 days ago1714164551IN
0xa2ccfE29...D2148122b
0 ETH0.00087086.12088933
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MinterFilterV2

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 25 runs

Other Settings:
default evmVersion
File 1 of 14 : MinterFilterV2.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

// @dev fixed to specific solidity version for clarity and for more clear
// source code verification purposes.
pragma solidity 0.8.19;

import {IMinterFilterV1} from "../../interfaces/v0.8.x/IMinterFilterV1.sol";
import {ISharedMinterV0} from "../../interfaces/v0.8.x/ISharedMinterV0.sol";
import {IGenArt721CoreContractV3_Base} from "../../interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol";
import {ICoreRegistryV1} from "../../interfaces/v0.8.x/ICoreRegistryV1.sol";
import {IAdminACLV0} from "../../interfaces/v0.8.x/IAdminACLV0.sol";

import {Bytes32Strings} from "../../libs/v0.8.x/Bytes32Strings.sol";

import {Ownable} from "@openzeppelin-4.7/contracts/access/Ownable.sol";
import {EnumerableMap} from "@openzeppelin-4.7/contracts/utils/structs/EnumerableMap.sol";
import {EnumerableSet} from "@openzeppelin-4.7/contracts/utils/structs/EnumerableSet.sol";
import {Math} from "@openzeppelin-4.7/contracts/utils/math/Math.sol";

/**
 * @title MinterFilterV2
 * @dev At the time of deployment, this contract is intended to be used with
 * core contracts that implement IGenArt721CoreContractV3_Base.
 * @author Art Blocks Inc.
 * @notice This Minter Filter V2 contract allows minters to be set on a
 * per-project basis, for any registered core contract. This minter filter does
 * not extend the previous version of the minter filters, as the previous
 * version is not compatible with multiple core contracts.
 *
 * This contract is designed to be managed by an Admin ACL contract, as well as
 * delegated privileges to core contract artists and Admin ACL contracts.
 * These roles hold extensive power and can arbitrarily control and modify
 * how a project's tokens may be minted.
 * Care must be taken to ensure that the admin ACL contract and artist
 * addresses are secure behind a multi-sig or other access control mechanism.
 * ----------------------------------------------------------------------------
 * The following functions are restricted as allowed by this contract's Admin
 * ACL:
 * - updateCoreRegistry
 * - approveMinterGlobally
 * - revokeMinterGlobally
 * ----------------------------------------------------------------------------
 * The following functions are restricted as allowed by each core contract's
 * Admin ACL contract:
 * - approveMinterForContract
 * - revokeMinterForContract
 * - removeMintersForProjectsOnContract
 * ----------------------------------------------------------------------------
 * The following functions are restricted as allowed by each core contract's
 * Admin ACL contract, or to the artist address of the project:
 * - setMinterForProject
 * - removeMinterForProject
 * ----------------------------------------------------------------------------
 * Additional admin and artist privileged roles may be described on minters,
 * registries, and other contracts that may interact with this contract.
 */
contract MinterFilterV2 is Ownable, IMinterFilterV1 {
    // add Enumerable Map, Enumerable Set methods
    using EnumerableMap for EnumerableMap.UintToAddressMap;
    using EnumerableSet for EnumerableSet.AddressSet;
    // add Bytes32Strings methods
    using Bytes32Strings for bytes32;

    /// @notice Version of this minter filter contract
    // @dev use function minterFilterVersion to get this as a string
    bytes32 constant MINTER_FILTER_VERSION = "v2.0.0";

    /// @notice Type of this minter filter contract
    // @dev use function minterFilterType to get this as a string
    bytes32 constant MINTER_FILTER_TYPE = "MinterFilterV2";

    /// @notice Admin ACL contract for this minter filter
    IAdminACLV0 public adminACLContract;

    /**
     * @notice Core registry, that tracks all registered core contracts
     */
    ICoreRegistryV1 public coreRegistry;

    /// @notice Minter address => qty projects across all core contracts currently
    /// using the minter
    mapping(address minterAddress => uint256 numProjects)
        public numProjectsUsingMinter;

    /**
     * @notice Enumerable Set of globally approved minters.
     * This is a Set of addresses that are approved to mint on any
     * project, for any core contract.
     * @dev note that contract admins can extend a separate Set of minters for
     * their core contract via the `approveMinterForContract` function.
     */
    EnumerableSet.AddressSet private _globallyApprovedMinters;

    /**
     * @notice Mapping of core contract addresses to Enumerable Sets of approved
     * minters for that core contract.
     * @dev note that contract admins can extend this Set for their core
     * contract by via the `approveMinterForContract` function, and can remove
     * minters from this Set via the `revokeMinterForContract` function.
     */
    mapping(address coreContract => EnumerableSet.AddressSet approvedMintersForContract)
        private _contractApprovedMinters;

    /**
     * @notice Mapping of core contract addresses to Enumerable Maps of project IDs to
     * minter addresses.
     */
    mapping(address coreContract => EnumerableMap.UintToAddressMap projectIdToMinterAddress)
        private _minterForProject;

    /**
     * @notice Function to validate an address is non-zero.
     * @param address_ Address to validate
     */
    function _onlyNonZeroAddress(address address_) internal pure {
        require(address_ != address(0), "Only non-zero address");
    }

    /**
     * @notice Function to restrict access to only AdminACL allowed calls
     * on this minter filter's admin ACL contract.
     * @param selector function selector to be checked
     */
    function _onlyAdminACL(bytes4 selector) internal {
        require(
            adminACLAllowed(msg.sender, address(this), selector),
            "Only Admin ACL allowed"
        );
    }

    /**
     * @notice Function to restrict access to only AdminACL allowed calls
     * on a given core contract.
     * @dev defers to the ACL contract used by the core contract
     * @param coreContract core contract address
     * @param selector function selector to be checked
     */
    function _onlyCoreAdminACL(address coreContract, bytes4 selector) internal {
        require(
            IGenArt721CoreContractV3_Base(coreContract).adminACLAllowed({
                _sender: msg.sender,
                _contract: address(this),
                _selector: selector
            }),
            "Only Core AdminACL allowed"
        );
    }

    /**
     * @notice Function to restrict access to only core AdminACL or the project artist.
     * @dev Defers to the ACL contract used by the core contract
     * @param coreContract core contract address
     * @param selector function selector to be checked
     */
    function _onlyCoreAdminACLOrArtist(
        uint256 projectId,
        address coreContract,
        bytes4 selector
    ) internal {
        IGenArt721CoreContractV3_Base genArtCoreContract_Base = IGenArt721CoreContractV3_Base(
                coreContract
            );
        require(
            (msg.sender ==
                genArtCoreContract_Base.projectIdToArtistAddress(projectId)) ||
                (
                    genArtCoreContract_Base.adminACLAllowed({
                        _sender: msg.sender,
                        _contract: address(this),
                        _selector: selector
                    })
                ),
            "Only Artist or Core Admin ACL"
        );
    }

    /**
     * @notice Function to restrict access to only core contracts registered with the
     * currently configured core registry. This is used to prevent non-registered core
     * contracts from being used with this minter filter.
     * @param coreContract core contract address
     */
    function _onlyRegisteredCoreContract(address coreContract) internal view {
        // @dev use core registry to check if core contract is registered
        require(
            coreRegistry.isRegisteredContract(coreContract),
            "Only registered core contract"
        );
    }

    /**
     * @notice Function to restrict access to only valid project IDs.
     * @param projectId Project ID to validate.
     * @param coreContract core contract address
     */
    function _onlyValidProjectId(
        uint256 projectId,
        address coreContract
    ) internal view {
        IGenArt721CoreContractV3_Base genArtCoreContract = IGenArt721CoreContractV3_Base(
                coreContract
            );
        require(
            (projectId >= genArtCoreContract.startingProjectId()) &&
                (projectId < genArtCoreContract.nextProjectId()),
            "Only valid project ID"
        );
    }

    /**
     * @notice Function to check if minter is globally approved or approved for a core contract.
     * @param coreContract core contract address
     * @param minter Minter to validate.
     */
    function _onlyApprovedMinter(
        address coreContract,
        address minter
    ) internal view {
        require(
            isApprovedMinterForContract({
                coreContract: coreContract,
                minter: minter
            }),
            "Only approved minters"
        );
    }

    /**
     * @notice Initializes contract to be a Minter for `genArt721Address`.
     * @param adminACLContract_ Address of admin access control contract, to be
     * set as contract owner.
     * @param coreRegistry_ Address of core registry contract.
     */
    constructor(address adminACLContract_, address coreRegistry_) {
        // set AdminACL management contract as owner
        _transferOwnership(adminACLContract_);
        // set core registry contract
        _updateCoreRegistry(coreRegistry_);
        emit Deployed();
    }

    /**
     * @notice returns the version of this minter filter contract
     */
    function minterFilterVersion() external pure returns (string memory) {
        return MINTER_FILTER_VERSION.toString();
    }

    /**
     * @notice returns the type of this minter filter contract
     */
    function minterFilterType() external pure returns (string memory) {
        return MINTER_FILTER_TYPE.toString();
    }

    /**
     * @notice Updates the core registry contract to be used by this contract.
     * Only callable as allowed by AdminACL of this contract.
     * @param coreRegistry_ Address of the new core registry contract.
     */
    function updateCoreRegistry(address coreRegistry_) external {
        _onlyAdminACL(this.updateCoreRegistry.selector);
        _updateCoreRegistry(coreRegistry_);
    }

    /**
     * @notice Globally approves minter `minter` to be available for
     * minting on any project, for any core contract.
     * Only callable as allowed by AdminACL of this contract.
     * @dev Reverts if minter is already globally approved, or does not
     * implement minterType().
     * @param minter Minter to be approved.
     */
    function approveMinterGlobally(address minter) external {
        _onlyAdminACL(this.approveMinterGlobally.selector);
        // @dev add() return true if the value was added to the set
        require(
            _globallyApprovedMinters.add(minter),
            "Minter already approved"
        );
        emit MinterApprovedGlobally({
            minter: minter,
            minterType: ISharedMinterV0(minter).minterType()
        });
    }

    /**
     * @notice Removes previously globally approved minter `minter`
     * from the list of globally approved minters.
     * Only callable as allowed by AdminACL of this contract.
     * Reverts if minter is not globally approved.
     * @dev intentionally do not check if minter is still in use by any
     * project, meaning that any projects currently using the minter will
     * continue to be able to use it. If existing projects should be forced
     * to discontinue using a minter, the minter may be removed by the minter
     * filter admin in bulk via the `removeMintersForProjectsOnContract`
     * function.
     * @param minter Minter to remove.
     */
    function revokeMinterGlobally(address minter) external {
        _onlyAdminACL(this.revokeMinterGlobally.selector);
        // @dev remove() returns true only if the value was already in the Set
        require(
            _globallyApprovedMinters.remove(minter),
            "Only previously approved minter"
        );
        emit MinterRevokedGlobally(minter);
    }

    /**
     * @notice Approves minter `minter` to be available for minting on
     * any project on core contarct `coreContract`.
     * Only callable as allowed by AdminACL of core contract `coreContract`.
     * Reverts if core contract is not registered, if minter is already
     * approved for the contract, or if minter does not implement minterType().
     * @param coreContract Core contract to approve minter for.
     * @param minter Minter to be approved.
     */
    function approveMinterForContract(
        address coreContract,
        address minter
    ) external {
        _onlyRegisteredCoreContract(coreContract);
        _onlyCoreAdminACL({
            coreContract: coreContract,
            selector: this.approveMinterForContract.selector
        });
        // @dev add() returns true if the value was added to the Set
        require(
            _contractApprovedMinters[coreContract].add(minter),
            "Minter already approved"
        );
        emit MinterApprovedForContract({
            coreContract: coreContract,
            minter: minter,
            minterType: ISharedMinterV0(minter).minterType()
        });
    }

    /**
     * @notice Removes previously approved minter `minter` from the
     * list of approved minters on core contract `coreContract`.
     * Only callable as allowed by AdminACL of core contract `coreContract`.
     * Reverts if core contract is not registered, or if minter is not approved
     * on contract.
     * @dev intentionally does not check if minter is still in use by any
     * project, meaning that any projects currently using the minter will
     * continue to be able to use it. If existing projects should be forced
     * to discontinue using a minter, the minter may be removed by the contract
     * admin in bulk via the `removeMintersForProjectsOnContract` function.
     * @param coreContract Core contract to remove minter from.
     * @param minter Minter to remove.
     */
    function revokeMinterForContract(
        address coreContract,
        address minter
    ) external {
        _onlyRegisteredCoreContract(coreContract);
        _onlyCoreAdminACL({
            coreContract: coreContract,
            selector: this.revokeMinterForContract.selector
        });
        // @dev intentionally do not check if minter is still in use by any
        // project, since it is possible that a different contract's project is
        // using the minter
        // @dev remove() returns true only if the value was already in the Set
        require(
            _contractApprovedMinters[coreContract].remove(minter),
            "Only previously approved minter"
        );
        emit MinterRevokedForContract({
            coreContract: coreContract,
            minter: minter
        });
    }

    /**
     * @notice Sets minter for project `projectId` on contract `coreContract`
     * to minter `minter`.
     * Only callable by the project's artist or as allowed by AdminACL of
     * core contract `coreContract`.
     * Reverts if:
     *  - core contract is not registered
     *  - minter is not approved globally on this minter filter or for the
     *    project's core contract
     *  - project is not valid on the core contract
     *  - function is called by an address other than the project's artist
     *    or a sender allowed by the core contract's admin ACL
     *  - minter does not implement minterType()
     * @param projectId Project ID to set minter for.
     * @param coreContract Core contract of project.
     * @param minter Minter to be the project's minter.
     */
    function setMinterForProject(
        uint256 projectId,
        address coreContract,
        address minter
    ) external {
        /// CHECKS
        _onlyRegisteredCoreContract(coreContract);
        _onlyCoreAdminACLOrArtist({
            projectId: projectId,
            coreContract: coreContract,
            selector: this.setMinterForProject.selector
        });
        _onlyApprovedMinter({coreContract: coreContract, minter: minter});
        _onlyValidProjectId({projectId: projectId, coreContract: coreContract});
        /// EFFECTS
        // decrement number of projects using a previous minter
        (bool hasPreviousMinter, address previousMinter) = _minterForProject[
            coreContract
        ].tryGet(projectId);
        if (hasPreviousMinter) {
            numProjectsUsingMinter[previousMinter]--;
        }
        // assign new minter
        numProjectsUsingMinter[minter]++;
        _minterForProject[coreContract].set(projectId, minter);
        emit ProjectMinterRegistered({
            projectId: projectId,
            coreContract: coreContract,
            minter: minter,
            minterType: ISharedMinterV0(minter).minterType()
        });
    }

    /**
     * @notice Updates project `projectId` on contract `coreContract` to have
     * no configured minter.
     * Only callable by the project's artist or as allowed by AdminACL of
     * core contract `coreContract`.
     * Reverts if:
     *  - core contract is not registered
     *  - project does not already have a minter assigned
     *  - function is called by an address other than the project's artist
     *    or a sender allowed by the core contract's admin ACL
     * @param projectId Project ID to remove minter for.
     * @param coreContract Core contract of project.
     * @dev requires project to have an assigned minter
     */
    function removeMinterForProject(
        uint256 projectId,
        address coreContract
    ) external {
        _onlyRegisteredCoreContract(coreContract);
        _onlyCoreAdminACLOrArtist({
            projectId: projectId,
            coreContract: coreContract,
            selector: this.removeMinterForProject.selector
        });
        // @dev this will revert if project does not have a minter
        _removeMinterForProject({
            projectId: projectId,
            coreContract: coreContract
        });
    }

    /**
     * @notice Updates an array of project IDs to have no configured minter.
     * Only callable as allowed by AdminACL of core contract `coreContract`.
     * Reverts if the core contract is not registered, or if any project does
     * not already have a minter assigned.
     * @param projectIds Array of project IDs to remove minters for.
     * @param coreContract Core contract of projects.
     * @dev caution with respect to single tx gas limits
     */
    function removeMintersForProjectsOnContract(
        uint256[] calldata projectIds,
        address coreContract
    ) external {
        _onlyRegisteredCoreContract(coreContract);
        _onlyCoreAdminACL({
            coreContract: coreContract,
            selector: this.removeMintersForProjectsOnContract.selector
        });
        uint256 numProjects = projectIds.length;
        for (uint256 i; i < numProjects; ) {
            _removeMinterForProject({
                projectId: projectIds[i],
                coreContract: coreContract
            });
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Mint a token from project `projectId` on contract
     * `coreContract` to `to`, originally purchased by `sender`.
     * @param to The new token's owner.
     * @param projectId Project ID to mint a new token on.
     * @param sender Address purchasing a new token.
     * @param coreContract Core contract of project.
     * @return tokenId Token ID of minted token
     * @dev reverts w/nonexistent key error when project has no assigned minter
     * @dev does not check if core contract is registered, for gas efficiency
     * and because project must have already been assigned a minter, which
     * requires the core contract to have been previously registered. If core
     * contract was unregistered but the project still has an assigned minter,
     * minting will remain possible.
     * @dev function name is optimized for gas.
     */
    function mint_joo(
        address to,
        uint256 projectId,
        address coreContract,
        address sender
    ) external returns (uint256 tokenId) {
        // CHECKS
        // minter is the project's minter
        require(
            msg.sender == _minterForProject[coreContract].get(projectId),
            "Only assigned minter"
        );
        // INTERACTIONS
        tokenId = IGenArt721CoreContractV3_Base(coreContract).mint_Ecf({
            _to: to,
            _projectId: projectId,
            _by: sender
        });
        return tokenId;
    }

    /**
     * @notice Gets the assigned minter for project `projectId` on core
     * contract `coreContract`.
     * Reverts if project does not have an assigned minter.
     * @param projectId Project ID to query.
     * @param coreContract Core contract of project.
     * @return address Minter address assigned to project
     * @dev requires project to have an assigned minter
     * @dev this function intentionally does not check that the core contract
     * is registered, since it must have been registered at the time the
     * project was assigned a minter
     */
    function getMinterForProject(
        uint256 projectId,
        address coreContract
    ) external view returns (address) {
        // @dev use tryGet to control revert message if no minter assigned
        (bool hasMinter, address currentMinter) = _minterForProject[
            coreContract
        ].tryGet(projectId);
        require(hasMinter, "No minter assigned");
        return currentMinter;
    }

    /**
     * @notice Queries if project `projectId` on core contract `coreContract`
     * has an assigned minter.
     * @param projectId Project ID to query.
     * @param coreContract Core contract of project.
     * @return bool true if project has an assigned minter, else false
     * @dev requires project to have an assigned minter
     * @dev this function intentionally does not check that the core contract
     * is registered, since it must have been registered at the time the
     * project was assigned a minter
     */
    function projectHasMinter(
        uint256 projectId,
        address coreContract
    ) external view returns (bool) {
        (bool hasMinter, ) = _minterForProject[coreContract].tryGet(projectId);
        return hasMinter;
    }

    /**
     * @notice Gets quantity of projects on a given core contract that have
     * assigned minters.
     * @param coreContract Core contract to query.
     * @return uint256 quantity of projects that have assigned minters
     * @dev this function intentionally does not check that the core contract
     * is registered, since it must have been registered at the time the
     * project was assigned a minter
     */
    function getNumProjectsOnContractWithMinters(
        address coreContract
    ) external view returns (uint256) {
        return _minterForProject[coreContract].length();
    }

    /**
     * @notice Get project ID and minter address at index `index` of
     * enumerable map.
     * @param coreContract Core contract to query.
     * @param index enumerable map index to query.
     * @return projectId project ID at index `index`
     * @return minterAddress minter address for project at index `index`
     * @return minterType minter type of minter at minterAddress
     * @dev index must be < quantity of projects that have assigned minters,
     * otherwise reverts
     * @dev reverts if minter does not implement minterType() function
     * @dev this function intentionally does not check that the core contract
     * is registered, since it must have been registered at the time the
     * project was assigned a minter
     */
    function getProjectAndMinterInfoOnContractAt(
        address coreContract,
        uint256 index
    )
        external
        view
        returns (
            uint256 projectId,
            address minterAddress,
            string memory minterType
        )
    {
        // @dev at() reverts if index is out of bounds
        (projectId, minterAddress) = _minterForProject[coreContract].at(index);
        minterType = ISharedMinterV0(minterAddress).minterType();
    }

    /**
     * @notice View that returns if a core contract is registered with the
     * core registry, allowing this minter filter to service it.
     * @param coreContract core contract address to be checked
     * @return bool true if core contract is registered, else false
     */
    function isRegisteredCoreContract(
        address coreContract
    ) external view override returns (bool) {
        return coreRegistry.isRegisteredContract(coreContract);
    }

    /**
     * @notice Gets all projects on core contract `coreContract` that are
     * using minter `minter`.
     * Warning: Unbounded gas limit. This function is gas-intensive and should
     * only be used for off-chain queries. Alternatively, the subgraph indexing
     * layer may be used to query these values.
     * @param coreContract core contract to query
     * @param minter minter to query
     */
    function getProjectsOnContractUsingMinter(
        address coreContract,
        address minter
    ) external view returns (uint256[] memory projectIds) {
        EnumerableMap.UintToAddressMap storage minterMap = _minterForProject[
            coreContract
        ];
        // initialize arrays with maximum potential length
        // @dev use lesser of num projects using minter across all contracts
        // and number of projects on the contract with minters assigned, since
        // both values represent an upper bound on the number of projects that
        // could be using the minter on the contract
        uint256 maxNumProjects = Math.min(
            numProjectsUsingMinter[minter],
            minterMap.length()
        );
        projectIds = new uint256[](maxNumProjects);
        // iterate over all projects on contract, adding to array if using
        // `minter`
        uint256 numProjects = minterMap.length();
        uint256 numProjectsOnContractUsingMinter;
        for (uint256 i; i < numProjects; ) {
            (uint256 projectId, address minter_) = minterMap.at(i);
            unchecked {
                if (minter_ == minter) {
                    projectIds[numProjectsOnContractUsingMinter++] = projectId;
                }
                ++i;
            }
        }
        // trim array if necessary
        if (maxNumProjects > numProjectsOnContractUsingMinter) {
            assembly ("memory-safe") {
                mstore(projectIds, numProjectsOnContractUsingMinter)
            }
        }
        return projectIds;
    }

    /**
     * @notice Gets all minters that are globally approved on this minter
     * filter. Returns an array of MinterWithType structs, which contain the
     * minter address and minter type.
     * This function has unbounded gas, and should only be used for off-chain
     * queries.
     * Alternatively, the subgraph indexing layer may be used to query these
     * values.
     * @return mintersWithTypes Array of MinterWithType structs, which contain
     * the minter address and minter type.
     */
    function getAllGloballyApprovedMinters()
        external
        view
        returns (MinterWithType[] memory mintersWithTypes)
    {
        // initialize arrays with appropriate length
        uint256 numMinters = _globallyApprovedMinters.length();
        mintersWithTypes = new MinterWithType[](numMinters);
        // iterate over all globally approved minters, adding to array
        for (uint256 i; i < numMinters; ) {
            address minterAddress = _globallyApprovedMinters.at(i);
            // @dev we know minterType() does not revert, because it was called
            // when globally approving the minter
            string memory minterType = ISharedMinterV0(minterAddress)
                .minterType();
            mintersWithTypes[i] = MinterWithType({
                minterAddress: minterAddress,
                minterType: minterType
            });
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Gets all minters that are approved for a specific core contract.
     * Returns an array of MinterWithType structs, which contain the minter
     * address and minter type.
     * This function has unbounded gas, and should only be used for off-chain
     * queries.
     * @param coreContract Core contract to query.
     * @return mintersWithTypes Array of MinterWithType structs, which contain
     * the minter address and minter type.
     */
    function getAllContractApprovedMinters(
        address coreContract
    ) external view returns (MinterWithType[] memory mintersWithTypes) {
        // initialize arrays with appropriate length
        EnumerableSet.AddressSet
            storage contractApprovedMinters = _contractApprovedMinters[
                coreContract
            ];
        uint256 numMinters = contractApprovedMinters.length();
        mintersWithTypes = new MinterWithType[](numMinters);
        // iterate over all minters approved for a given contract, adding to
        // array
        for (uint256 i; i < numMinters; ) {
            address minterAddress = contractApprovedMinters.at(i);
            // @dev we know minterType() does not revert, because it was called
            // when approving the minter for a contract
            string memory minterType = ISharedMinterV0(minterAddress)
                .minterType();
            mintersWithTypes[i] = MinterWithType({
                minterAddress: minterAddress,
                minterType: minterType
            });
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Convenience function that returns whether `sender` is allowed
     * to call function with selector `selector` on contract `contract`, as
     * determined by this contract's current Admin ACL contract. Expected use
     * cases include minter contracts checking if caller is allowed to call
     * admin-gated functions on minter contracts.
     * @param sender Address of the sender calling function with selector
     * `selector` on contract `contract`.
     * @param contract_ Address of the contract being called by `sender`.
     * @param selector Function selector of the function being called by
     * `sender`.
     * @return bool Whether `sender` is allowed to call function with selector
     * `selector` on contract `contract`.
     * @dev assumes the Admin ACL contract is the owner of this contract, which
     * is expected to always be true.
     * @dev adminACLContract is expected to not be null address (owner cannot
     * renounce ownership on this contract), and conform to IAdminACLV0
     * interface.
     */
    function adminACLAllowed(
        address sender,
        address contract_,
        bytes4 selector
    ) public returns (bool) {
        return
            adminACLContract.allowed({
                _sender: sender,
                _contract: contract_,
                _selector: selector
            });
    }

    /**
     * @notice Returns whether `minter` is globally approved to mint tokens
     * on any contract.
     * @param minter Address of minter to check.
     */
    function isGloballyApprovedMinter(
        address minter
    ) public view returns (bool) {
        return _globallyApprovedMinters.contains(minter);
    }

    /**
     * @notice Returns whether `minter` is approved to mint tokens on
     * core contract `coreContract`.
     * @param coreContract Address of core contract to check.
     * @param minter Address of minter to check.
     */
    function isApprovedMinterForContract(
        address coreContract,
        address minter
    ) public view returns (bool) {
        return
            isGloballyApprovedMinter(minter) ||
            _contractApprovedMinters[coreContract].contains(minter);
    }

    /**
     * @notice Returns contract owner. Set to deployer's address by default on
     * contract deployment.
     * @return address Address of contract owner.
     * @dev ref: https://docs.openzeppelin.com/contracts/4.x/api/access#Ownable
     * @dev owner role was called `admin` prior to V3 core contract
     */
    function owner()
        public
        view
        override(Ownable, IMinterFilterV1)
        returns (address)
    {
        return Ownable.owner();
    }

    /// @dev override to prevent renouncing ownership
    /// @dev not permission gated since this immediately reverts
    function renounceOwnership() public pure override {
        revert("Cannot renounce ownership");
    }

    /**
     * @notice Updates project `projectId` to have no configured minter
     * Reverts if project does not already have an assigned minter.
     * @param projectId Project ID to remove minter.
     * @param coreContract Core contract of project.
     * @dev requires project to have an assigned minter
     * @dev this function intentionally does not check that the core contract
     * is registered, since it must have been registered at the time the
     * project was assigned a minter
     */
    function _removeMinterForProject(
        uint256 projectId,
        address coreContract
    ) internal {
        // remove minter for project and emit
        // @dev `minterForProject.get()` reverts tx if no minter set for project
        numProjectsUsingMinter[
            _minterForProject[coreContract].get(projectId, "No minter assigned")
        ]--;
        _minterForProject[coreContract].remove(projectId);
        emit ProjectMinterRemoved({
            projectId: projectId,
            coreContract: coreContract
        });
    }

    /**
     * @notice Transfers ownership of the contract to a new account (`owner`).
     * Internal function without access restriction.
     * @param owner_ New owner.
     * @dev owner role was called `admin` prior to V3 core contract.
     * @dev Overrides and wraps OpenZeppelin's _transferOwnership function to
     * also update adminACLContract for improved introspection.
     */
    function _transferOwnership(address owner_) internal override {
        Ownable._transferOwnership(owner_);
        adminACLContract = IAdminACLV0(owner_);
    }

    /**
     * @notice Updates this contract's core registry contract to
     * `coreRegistry`.
     * @param coreRegistry_ New core registry contract address.
     */
    function _updateCoreRegistry(address coreRegistry_) internal {
        _onlyNonZeroAddress(coreRegistry_);
        coreRegistry = ICoreRegistryV1(coreRegistry_);
        emit CoreRegistryUpdated(coreRegistry_);
    }
}

File 2 of 14 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _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 3 of 14 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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;
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns the square root of a number. It 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)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
        // good first aproximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 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) {
        uint256 result = sqrt(a);
        if (rounding == Rounding.Up && result * result < a) {
            result += 1;
        }
        return result;
    }
}

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

pragma solidity ^0.8.0;

import "./EnumerableSet.sol";

/**
 * @dev Library for managing an enumerable variant of Solidity's
 * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
 * type.
 *
 * Maps have the following properties:
 *
 * - Entries are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Entries are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableMap for EnumerableMap.UintToAddressMap;
 *
 *     // Declare a set state variable
 *     EnumerableMap.UintToAddressMap private myMap;
 * }
 * ```
 *
 * The following map types are supported:
 *
 * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
 * - `address -> uint256` (`AddressToUintMap`) since v4.6.0
 * - `bytes32 -> bytes32` (`Bytes32ToBytes32`) since v4.6.0
 * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
 * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an array of EnumerableMap.
 * ====
 */
library EnumerableMap {
    using EnumerableSet for EnumerableSet.Bytes32Set;

    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Map type with
    // bytes32 keys and values.
    // The Map implementation uses private functions, and user-facing
    // implementations (such as Uint256ToAddressMap) are just wrappers around
    // the underlying Map.
    // This means that we can only create new EnumerableMaps for types that fit
    // in bytes32.

    struct Bytes32ToBytes32Map {
        // Storage of keys
        EnumerableSet.Bytes32Set _keys;
        mapping(bytes32 => bytes32) _values;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(
        Bytes32ToBytes32Map storage map,
        bytes32 key,
        bytes32 value
    ) internal returns (bool) {
        map._values[key] = value;
        return map._keys.add(key);
    }

    /**
     * @dev Removes a key-value pair from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
        delete map._values[key];
        return map._keys.remove(key);
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
        return map._keys.contains(key);
    }

    /**
     * @dev Returns the number of key-value pairs in the map. O(1).
     */
    function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
        return map._keys.length();
    }

    /**
     * @dev Returns the key-value pair stored at position `index` in the map. O(1).
     *
     * Note that there are no guarantees on the ordering of entries inside the
     * array, and it may change when more entries are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
        bytes32 key = map._keys.at(index);
        return (key, map._values[key]);
    }

    /**
     * @dev Tries to returns the value associated with `key`.  O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
        bytes32 value = map._values[key];
        if (value == bytes32(0)) {
            return (contains(map, key), bytes32(0));
        } else {
            return (true, value);
        }
    }

    /**
     * @dev Returns the value associated with `key`.  O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
        bytes32 value = map._values[key];
        require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key");
        return value;
    }

    /**
     * @dev Same as {_get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {_tryGet}.
     */
    function get(
        Bytes32ToBytes32Map storage map,
        bytes32 key,
        string memory errorMessage
    ) internal view returns (bytes32) {
        bytes32 value = map._values[key];
        require(value != 0 || contains(map, key), errorMessage);
        return value;
    }

    // UintToUintMap

    struct UintToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(
        UintToUintMap storage map,
        uint256 key,
        uint256 value
    ) internal returns (bool) {
        return set(map._inner, bytes32(key), bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
        return remove(map._inner, bytes32(key));
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
        return contains(map._inner, bytes32(key));
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the set. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
        (bytes32 key, bytes32 value) = at(map._inner, index);
        return (uint256(key), uint256(value));
    }

    /**
     * @dev Tries to returns the value associated with `key`.  O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
        (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
        return (success, uint256(value));
    }

    /**
     * @dev Returns the value associated with `key`.  O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(key)));
    }

    /**
     * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
    function get(
        UintToUintMap storage map,
        uint256 key,
        string memory errorMessage
    ) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(key), errorMessage));
    }

    // UintToAddressMap

    struct UintToAddressMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(
        UintToAddressMap storage map,
        uint256 key,
        address value
    ) internal returns (bool) {
        return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
        return remove(map._inner, bytes32(key));
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
        return contains(map._inner, bytes32(key));
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToAddressMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the set. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
        (bytes32 key, bytes32 value) = at(map._inner, index);
        return (uint256(key), address(uint160(uint256(value))));
    }

    /**
     * @dev Tries to returns the value associated with `key`.  O(1).
     * Does not revert if `key` is not in the map.
     *
     * _Available since v3.4._
     */
    function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
        (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
        return (success, address(uint160(uint256(value))));
    }

    /**
     * @dev Returns the value associated with `key`.  O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
        return address(uint160(uint256(get(map._inner, bytes32(key)))));
    }

    /**
     * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
    function get(
        UintToAddressMap storage map,
        uint256 key,
        string memory errorMessage
    ) internal view returns (address) {
        return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage))));
    }

    // AddressToUintMap

    struct AddressToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(
        AddressToUintMap storage map,
        address key,
        uint256 value
    ) internal returns (bool) {
        return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(AddressToUintMap storage map, address key) internal returns (bool) {
        return remove(map._inner, bytes32(uint256(uint160(key))));
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
        return contains(map._inner, bytes32(uint256(uint160(key))));
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(AddressToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the set. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
        (bytes32 key, bytes32 value) = at(map._inner, index);
        return (address(uint160(uint256(key))), uint256(value));
    }

    /**
     * @dev Tries to returns the value associated with `key`.  O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
        (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
        return (success, uint256(value));
    }

    /**
     * @dev Returns the value associated with `key`.  O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
    }

    /**
     * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
    function get(
        AddressToUintMap storage map,
        address key,
        string memory errorMessage
    ) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage));
    }

    // Bytes32ToUintMap

    struct Bytes32ToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(
        Bytes32ToUintMap storage map,
        bytes32 key,
        uint256 value
    ) internal returns (bool) {
        return set(map._inner, key, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
        return remove(map._inner, key);
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
        return contains(map._inner, key);
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the set. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
        (bytes32 key, bytes32 value) = at(map._inner, index);
        return (key, uint256(value));
    }

    /**
     * @dev Tries to returns the value associated with `key`.  O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
        (bool success, bytes32 value) = tryGet(map._inner, key);
        return (success, uint256(value));
    }

    /**
     * @dev Returns the value associated with `key`.  O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
        return uint256(get(map._inner, key));
    }

    /**
     * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
    function get(
        Bytes32ToUintMap storage map,
        bytes32 key,
        string memory errorMessage
    ) internal view returns (uint256) {
        return uint256(get(map._inner, key, errorMessage));
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 7 of 14 : IAdminACLV0.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

interface IAdminACLV0 {
    /**
     * @notice Token ID `_tokenId` minted to `_to`.
     * @param previousSuperAdmin The previous superAdmin address.
     * @param newSuperAdmin The new superAdmin address.
     * @param genArt721CoreAddressesToUpdate Array of genArt721Core
     * addresses to update to the new superAdmin, for indexing purposes only.
     */
    event SuperAdminTransferred(
        address indexed previousSuperAdmin,
        address indexed newSuperAdmin,
        address[] genArt721CoreAddressesToUpdate
    );

    /// Type of the Admin ACL contract, e.g. "AdminACLV0"
    function AdminACLType() external view returns (string memory);

    /// super admin address
    function superAdmin() external view returns (address);

    /**
     * @notice Calls transferOwnership on other contract from this contract.
     * This is useful for updating to a new AdminACL contract.
     * @dev this function should be gated to only superAdmin-like addresses.
     */
    function transferOwnershipOn(
        address _contract,
        address _newAdminACL
    ) external;

    /**
     * @notice Calls renounceOwnership on other contract from this contract.
     * @dev this function should be gated to only superAdmin-like addresses.
     */
    function renounceOwnershipOn(address _contract) external;

    /**
     * @notice Checks if sender `_sender` is allowed to call function with selector
     * `_selector` on contract `_contract`.
     */
    function allowed(
        address _sender,
        address _contract,
        bytes4 _selector
    ) external returns (bool);
}

File 8 of 14 : ICoreRegistryV1.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;

import "./IEngineRegistryV0.sol";

interface ICoreRegistryV1 is IEngineRegistryV0 {
    function registerContracts(
        address[] calldata contractAddresses,
        bytes32[] calldata coreVersions,
        bytes32[] calldata coreTypes
    ) external;

    function unregisterContracts(address[] calldata contractAddresses) external;

    function getNumRegisteredContracts() external view returns (uint256);

    function getRegisteredContractAt(
        uint256 index
    ) external view returns (address);

    function isRegisteredContract(
        address contractAddress
    ) external view returns (bool isRegistered);
}

File 9 of 14 : IEngineRegistryV0.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;

interface IEngineRegistryV0 {
    /// ADDRESS
    /**
     * @notice contract has been registered as a contract that is powered by the Art Blocks Engine.
     */
    event ContractRegistered(
        address indexed _contractAddress,
        bytes32 _coreVersion,
        bytes32 _coreType
    );

    /// ADDRESS
    /**
     * @notice contract has been unregistered as a contract that is powered by the Art Blocks Engine.
     */
    event ContractUnregistered(address indexed _contractAddress);

    /**
     * @notice Emits a `ContractRegistered` event with the provided information.
     * @dev this function should be gated to only deployer addresses.
     */
    function registerContract(
        address _contractAddress,
        bytes32 _coreVersion,
        bytes32 _coreType
    ) external;

    /**
     * @notice Emits a `ContractUnregistered` event with the provided information, validating that the provided
     *         address was indeed previously registered.
     * @dev this function should be gated to only deployer addresses.
     */
    function unregisterContract(address _contractAddress) external;
}

File 10 of 14 : IGenArt721CoreContractV3_Base.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

import "./IAdminACLV0.sol";
/// use the Royalty Registry's IManifold interface for token royalties
import "./IManifold.sol";

/**
 * @title This interface is intended to house interface items that are common
 * across all GenArt721CoreContractV3 flagship and derivative implementations.
 * This interface extends the IManifold royalty interface in order to
 * add support the Royalty Registry by default.
 * @author Art Blocks Inc.
 */
interface IGenArt721CoreContractV3_Base is IManifold {
    /**
     * @notice Token ID `_tokenId` minted to `_to`.
     */
    event Mint(address indexed _to, uint256 indexed _tokenId);

    /**
     * @notice currentMinter updated to `_currentMinter`.
     * @dev Implemented starting with V3 core
     */
    event MinterUpdated(address indexed _currentMinter);

    /**
     * @notice Platform updated on bytes32-encoded field `_field`.
     */
    event PlatformUpdated(bytes32 indexed _field);

    /**
     * @notice Project ID `_projectId` updated on bytes32-encoded field
     * `_update`.
     */
    event ProjectUpdated(uint256 indexed _projectId, bytes32 indexed _update);

    event ProposedArtistAddressesAndSplits(
        uint256 indexed _projectId,
        address _artistAddress,
        address _additionalPayeePrimarySales,
        uint256 _additionalPayeePrimarySalesPercentage,
        address _additionalPayeeSecondarySales,
        uint256 _additionalPayeeSecondarySalesPercentage
    );

    event AcceptedArtistAddressesAndSplits(uint256 indexed _projectId);

    // version and type of the core contract
    // coreVersion is a string of the form "0.x.y"
    function coreVersion() external view returns (string memory);

    // coreType is a string of the form "GenArt721CoreV3"
    function coreType() external view returns (string memory);

    // owner (pre-V3 was named admin) of contract
    // this is expected to be an Admin ACL contract for V3
    function owner() external view returns (address);

    // Admin ACL contract for V3, will be at the address owner()
    function adminACLContract() external returns (IAdminACLV0);

    // backwards-compatible (pre-V3) admin - equal to owner()
    function admin() external view returns (address);

    /**
     * Function determining if _sender is allowed to call function with
     * selector _selector on contract `_contract`. Intended to be used with
     * peripheral contracts such as minters, as well as internally by the
     * core contract itself.
     */
    function adminACLAllowed(
        address _sender,
        address _contract,
        bytes4 _selector
    ) external returns (bool);

    /// getter function of public variable
    function startingProjectId() external view returns (uint256);

    // getter function of public variable
    function nextProjectId() external view returns (uint256);

    // getter function of public mapping
    function tokenIdToProjectId(
        uint256 tokenId
    ) external view returns (uint256 projectId);

    // @dev this is not available in V0
    function isMintWhitelisted(address minter) external view returns (bool);

    function projectIdToArtistAddress(
        uint256 _projectId
    ) external view returns (address payable);

    function projectIdToAdditionalPayeePrimarySales(
        uint256 _projectId
    ) external view returns (address payable);

    function projectIdToAdditionalPayeePrimarySalesPercentage(
        uint256 _projectId
    ) external view returns (uint256);

    function projectIdToSecondaryMarketRoyaltyPercentage(
        uint256 _projectId
    ) external view returns (uint256);

    function projectURIInfo(
        uint256 _projectId
    ) external view returns (string memory projectBaseURI);

    // @dev new function in V3
    function projectStateData(
        uint256 _projectId
    )
        external
        view
        returns (
            uint256 invocations,
            uint256 maxInvocations,
            bool active,
            bool paused,
            uint256 completedTimestamp,
            bool locked
        );

    function projectDetails(
        uint256 _projectId
    )
        external
        view
        returns (
            string memory projectName,
            string memory artist,
            string memory description,
            string memory website,
            string memory license
        );

    function projectScriptDetails(
        uint256 _projectId
    )
        external
        view
        returns (
            string memory scriptTypeAndVersion,
            string memory aspectRatio,
            uint256 scriptCount
        );

    function projectScriptByIndex(
        uint256 _projectId,
        uint256 _index
    ) external view returns (string memory);

    function tokenIdToHash(uint256 _tokenId) external view returns (bytes32);

    // function to set a token's hash (must be guarded)
    function setTokenHash_8PT(uint256 _tokenId, bytes32 _hash) external;

    // @dev gas-optimized signature in V3 for `mint`
    function mint_Ecf(
        address _to,
        uint256 _projectId,
        address _by
    ) external returns (uint256 tokenId);
}

File 11 of 14 : IManifold.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @dev Royalty Registry interface, used to support the Royalty Registry.
/// @dev Source: https://github.com/manifoldxyz/royalty-registry-solidity/blob/main/contracts/specs/IManifold.sol

/// @author: manifold.xyz

/**
 * @dev Royalty interface for creator core classes
 */
interface IManifold {
    /**
     * @dev Get royalites of a token.  Returns list of receivers and basisPoints
     *
     *  bytes4(keccak256('getRoyalties(uint256)')) == 0xbb3bafd6
     *
     *  => 0xbb3bafd6 = 0xbb3bafd6
     */
    function getRoyalties(
        uint256 tokenId
    ) external view returns (address payable[] memory, uint256[] memory);
}

File 12 of 14 : IMinterFilterV1.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

import "./ICoreRegistryV1.sol";
import "./IAdminACLV0.sol";

/**
 * @title IMinterFilterV1
 * @author Art Blocks Inc.
 * @notice Interface for a new minter filter contract.
 * This interface does not extend the previous version of the minter filter
 * interface, as the previous version is not compatible with the new
 * minter filter architecture.
 * @dev This interface is for a minter filter that supports multiple core
 * contracts, and allows for a minter to be set on a per-project basis.
 */
interface IMinterFilterV1 {
    /**
     * @notice Emitted when contract is deployed to notify indexing services
     * of the new contract deployment.
     */
    event Deployed();

    /**
     * @notice Globally approved minter `minter`.
     */
    event MinterApprovedGlobally(address indexed minter, string minterType);

    /**
     * @notice Globally revoked minter `minter`.
     * @dev contract owner may still approve this minter on a per-contract
     * basis.
     */
    event MinterRevokedGlobally(address indexed minter);

    /**
     * @notice Approved minter `minter` on core contract
     * `coreContract`.
     */
    event MinterApprovedForContract(
        address indexed coreContract,
        address indexed minter,
        string minterType
    );

    /**
     * @notice Revoked minter `minter` on core contract `coreContract`.
     * @dev minter filter owner may still globally approve this minter for all
     * contracts.
     */
    event MinterRevokedForContract(
        address indexed coreContract,
        address indexed minter
    );

    /**
     * @notice Minter at address `minter` set as minter for project
     * `projectId` on core contract `coreContract`.
     */
    event ProjectMinterRegistered(
        uint256 indexed projectId,
        address indexed coreContract,
        address indexed minter,
        string minterType
    );

    /**
     * @notice Minter removed for project `projectId` on core contract
     * `coreContract`.
     */
    event ProjectMinterRemoved(
        uint256 indexed projectId,
        address indexed coreContract
    );

    /**
     * @notice Admin ACL contract updated to `adminACLContract`.
     */
    event AdminACLUpdated(address indexed adminACLContract);

    /**
     * @notice Core Registry contract updated to `coreRegistry`.
     */
    event CoreRegistryUpdated(address indexed coreRegistry);

    // struct used to return minter info
    // @dev this is not used for storage of data
    struct MinterWithType {
        address minterAddress;
        string minterType;
    }

    function setMinterForProject(
        uint256 projectId,
        address coreContract,
        address minter
    ) external;

    function removeMinterForProject(
        uint256 projectId,
        address coreContract
    ) external;

    // @dev function name is optimized for gas
    function mint_joo(
        address to,
        uint256 projectId,
        address coreContract,
        address sender
    ) external returns (uint256);

    function updateCoreRegistry(address coreRegistry) external;

    /**
     * @notice Returns if `sender` is allowed to call function on `contract`
     * with `selector` selector, according to the MinterFilter's Admin ACL.
     */
    function adminACLAllowed(
        address sender,
        address contract_,
        bytes4 selector
    ) external returns (bool);

    function minterFilterType() external pure returns (string memory);

    function getMinterForProject(
        uint256 projectId,
        address coreContract
    ) external view returns (address);

    function projectHasMinter(
        uint256 projectId,
        address coreContract
    ) external view returns (bool);

    /**
     * @notice View that returns if a core contract is registered with the
     * core registry, allowing this minter filter to service it.
     * @param coreContract core contract address to be checked
     */
    function isRegisteredCoreContract(
        address coreContract
    ) external view returns (bool);

    /// Address of current core registry contract
    function coreRegistry() external view returns (ICoreRegistryV1);

    /// The current admin ACL contract
    function adminACLContract() external view returns (IAdminACLV0);

    /// The quantity of projects on a core contract that have assigned minters
    function getNumProjectsOnContractWithMinters(
        address coreContract
    ) external view returns (uint256);

    function getProjectAndMinterInfoOnContractAt(
        address coreContract,
        uint256 index
    )
        external
        view
        returns (
            uint256 projectId,
            address minterAddress,
            string memory minterType
        );

    function getAllGloballyApprovedMinters()
        external
        view
        returns (MinterWithType[] memory mintersWithTypes);

    function getAllContractApprovedMinters(
        address coreContract
    ) external view returns (MinterWithType[] memory mintersWithTypes);

    /**
     * Owner of contract.
     * @dev This returns the address of the Admin ACL contract.
     */
    function owner() external view returns (address);
}

File 13 of 14 : ISharedMinterV0.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

interface ISharedMinterV0 {
    // Sets the local max invocations for a given project, checking that the provided max invocations is
    // less than or equal to the global max invocations for the project set on the core contract.
    // This does not impact the max invocations value defined on the core contract.
    function manuallyLimitProjectMaxInvocations(
        uint256 projectId,
        address coreContract,
        uint24 maxInvocations
    ) external;

    // Called to make the minter contract aware of the max invocations for a
    // given project.
    function syncProjectMaxInvocationsToCore(
        uint256 projectId,
        address coreContract
    ) external;

    // getter function of public variable
    function minterType() external view returns (string memory);

    function minterFilterAddress() external returns (address);

    // Gets if token price is configured, token price in wei, currency symbol,
    // and currency address, assuming this is project's minter.
    // Supersedes any defined core price.
    function getPriceInfo(
        uint256 projectId,
        address coreContract
    )
        external
        view
        returns (
            bool isConfigured,
            uint256 tokenPriceInWei,
            string memory currencySymbol,
            address currencyAddress
        );
}

File 14 of 14 : Bytes32Strings.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
// Inspired by: https://ethereum.stackexchange.com/a/123950/103422

pragma solidity ^0.8.0;

/**
 * @dev Operations on bytes32 data type, dealing with conversion to string.
 */
library Bytes32Strings {
    /**
     * @notice Intended to convert a `bytes32`-encoded string literal to `string`.
     * Trims zero padding to arrive at original string literal.
     */
    function toString(
        bytes32 source
    ) internal pure returns (string memory result) {
        uint8 length;
        while (source[length] != 0 && length < 32) {
            length++;
        }
        assembly {
            // free memory pointer
            result := mload(0x40)
            // update free memory pointer to new "memory end"
            // (offset is 64-bytes: 32 for length, 32 for data)
            mstore(0x40, add(result, 0x40))
            // store length in first 32-byte memory slot
            mstore(result, length)
            // write actual data in second 32-byte memory slot
            mstore(add(result, 0x20), source)
        }
    }

    /**
     * @notice Intended to check if a `bytes32`-encoded string contains a given
     * character with UTF-8 character code `utf8CharCode exactly `targetQty`
     * times. Does not support searching for multi-byte characters, only
     * characters with UTF-8 character codes < 0x80.
     */
    function containsExactCharacterQty(
        bytes32 source,
        uint8 utf8CharCode,
        uint8 targetQty
    ) internal pure returns (bool) {
        uint8 _occurrences;
        uint8 i;
        for (i; i < 32; ) {
            uint8 _charCode = uint8(source[i]);
            // if not a null byte, or a multi-byte UTF-8 character, check match
            if (_charCode != 0 && _charCode < 0x80) {
                if (_charCode == utf8CharCode) {
                    unchecked {
                        // no risk of overflow since max 32 iterations < max uin8=255
                        ++_occurrences;
                    }
                }
            }
            unchecked {
                // no risk of overflow since max 32 iterations < max uin8=255
                ++i;
            }
        }
        return _occurrences == targetQty;
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"adminACLContract_","type":"address"},{"internalType":"address","name":"coreRegistry_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"adminACLContract","type":"address"}],"name":"AdminACLUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"coreRegistry","type":"address"}],"name":"CoreRegistryUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"Deployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"string","name":"minterType","type":"string"}],"name":"MinterApprovedForContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"string","name":"minterType","type":"string"}],"name":"MinterApprovedGlobally","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":true,"internalType":"address","name":"minter","type":"address"}],"name":"MinterRevokedForContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"}],"name":"MinterRevokedGlobally","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"string","name":"minterType","type":"string"}],"name":"ProjectMinterRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"}],"name":"ProjectMinterRemoved","type":"event"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"contract_","type":"address"},{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"adminACLAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"adminACLContract","outputs":[{"internalType":"contract IAdminACLV0","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"address","name":"minter","type":"address"}],"name":"approveMinterForContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"approveMinterGlobally","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"coreRegistry","outputs":[{"internalType":"contract ICoreRegistryV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"}],"name":"getAllContractApprovedMinters","outputs":[{"components":[{"internalType":"address","name":"minterAddress","type":"address"},{"internalType":"string","name":"minterType","type":"string"}],"internalType":"struct IMinterFilterV1.MinterWithType[]","name":"mintersWithTypes","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllGloballyApprovedMinters","outputs":[{"components":[{"internalType":"address","name":"minterAddress","type":"address"},{"internalType":"string","name":"minterType","type":"string"}],"internalType":"struct IMinterFilterV1.MinterWithType[]","name":"mintersWithTypes","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"getMinterForProject","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"}],"name":"getNumProjectsOnContractWithMinters","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getProjectAndMinterInfoOnContractAt","outputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"minterAddress","type":"address"},{"internalType":"string","name":"minterType","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"address","name":"minter","type":"address"}],"name":"getProjectsOnContractUsingMinter","outputs":[{"internalType":"uint256[]","name":"projectIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"address","name":"minter","type":"address"}],"name":"isApprovedMinterForContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"isGloballyApprovedMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"}],"name":"isRegisteredCoreContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"address","name":"sender","type":"address"}],"name":"mint_joo","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minterFilterType","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"minterFilterVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"minterAddress","type":"address"}],"name":"numProjectsUsingMinter","outputs":[{"internalType":"uint256","name":"numProjects","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"projectHasMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"removeMinterForProject","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"projectIds","type":"uint256[]"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"removeMintersForProjectsOnContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"address","name":"minter","type":"address"}],"name":"revokeMinterForContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"revokeMinterGlobally","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"address","name":"minter","type":"address"}],"name":"setMinterForProject","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"coreRegistry_","type":"address"}],"name":"updateCoreRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b506040516200245b3803806200245b8339810160408190526200003491620001d3565b6200003f3362000086565b6200004a8262000086565b6200005581620000b3565b6040517f3fad920548ed9f22deb8333b4cc1e4f9bc36666a1c2aa30ad59a0a3bb9dcbb9290600090a150506200020b565b620000918162000108565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b620000be8162000158565b600280546001600160a01b0319166001600160a01b0383169081179091556040517fb7b620c2c1cece07219b2ac19100c3f8629a9752c3c5484ed6524e99d0bddeed90600090a250565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038116620001b35760405162461bcd60e51b815260206004820152601560248201527f4f6e6c79206e6f6e2d7a65726f20616464726573730000000000000000000000604482015260640160405180910390fd5b50565b80516001600160a01b0381168114620001ce57600080fd5b919050565b60008060408385031215620001e757600080fd5b620001f283620001b6565b91506200020260208401620001b6565b90509250929050565b612240806200021b6000396000f3fe608060405234801561001057600080fd5b50600436106101625760003560e01c8063765aabff116100c9578063765aabff1461029c57806384b19f1e146102af5780638da5cb5b146102c25780638e5b9056146102ca5780638f48e2c0146102dd57806395139f66146102f0578063969c3ed714610303578063a02b095214610323578063a365254714610343578063af439a311461034b578063e420c9541461036d578063ea6e376114610380578063f2fde38b14610393578063fd9ca435146103a657600080fd5b80615f34146101675780630a3eecb21461018d5780631e9bef46146101a25780631f8608c6146101c257806320f2ccb7146101e5578063230448b1146101fa57806326df17b51461020d57806341946f0f1461022d57806353114de4146102405780635845de1f146102535780635e94e3de146102665780636bf3d05a146102795780637136b93e1461028c578063715018a614610294575b600080fd5b61017a610175366004611c52565b6103b9565b6040519081526020015b60405180910390f35b6101a061019b366004611ca5565b6104b7565b005b6001546101b5906001600160a01b031681565b6040516101849190611cde565b6101d56101d0366004611cf2565b610553565b6040519015158152602001610184565b6101ed6105cb565b6040516101849190611d5f565b6101d5610208366004611d72565b6105ec565b61017a61021b366004611cf2565b60036020526000908152604090205481565b6101a061023b366004611dca565b61066b565b6101a061024e366004611e01565b6107f7565b6101b5610261366004611e7b565b61084e565b6101a0610274366004611ca5565b6108ba565b6002546101b5906001600160a01b031681565b6101ed6109c6565b6101a06109da565b6101a06102aa366004611e7b565b610a1e565b6101a06102bd366004611cf2565b610a47565b6101b5610b27565b6101d56102d8366004611ca5565b610b36565b6101d56102eb366004611cf2565b610b70565b6101a06102fe366004611cf2565b610b7d565b610316610311366004611ca5565b610b99565b6040516101849190611ea0565b610336610331366004611cf2565b610ca9565b6040516101849190611ee4565b610336610e00565b61035e610359366004611f61565b610f3f565b60405161018493929190611f8d565b6101a061037b366004611cf2565b610fdd565b6101d561038e366004611e7b565b61104b565b6101a06103a1366004611cf2565b611078565b61017a6103b4366004611cf2565b6110ee565b6001600160a01b03821660009081526007602052604081206103db908561110f565b6001600160a01b0316336001600160a01b0316146104375760405162461bcd60e51b815260206004820152601460248201527327b7363c9030b9b9b4b3b732b21036b4b73a32b960611b60448201526064015b60405180910390fd5b604051615de560e01b81526001600160a01b038681166004830152602482018690528381166044830152841690615de5906064016020604051808303816000875af115801561048a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104ae9190611fb7565b95945050505050565b6104c08261111b565b6104d18263051f765960e11b6111d8565b6001600160a01b03821660009081526006602052604090206104f39082611294565b61050f5760405162461bcd60e51b815260040161042e90611fd0565b806001600160a01b0316826001600160a01b03167f60ecff7c83062c43d6aae585dca525293afec7a4f4c378d12927ce8f7525da2460405160405180910390a35050565b60025460405163f1052af560e01b81526000916001600160a01b03169063f1052af590610584908590600401611cde565b602060405180830381865afa1580156105a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c59190612007565b92915050565b60606105e76d26b4b73a32b92334b63a32b92b1960911b6112a9565b905090565b6001546040516217798b60e61b81526000916001600160a01b0316906305de62c09061062090879087908790600401612029565b6020604051808303816000875af115801561063f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106639190612007565b949350505050565b6106748261111b565b61068683836341946f0f60e01b611314565b610690828261145e565b61069a83836114ac565b6001600160a01b038216600090815260076020526040812081906106be90866115cb565b9150915081156106f2576001600160a01b03811660009081526003602052604081208054916106ec8361206c565b91905055505b6001600160a01b038316600090815260036020526040812080549161071683612083565b90915550506001600160a01b038416600090815260076020526040902061073e9086856115e9565b50826001600160a01b0316846001600160a01b0316867ff012149282a4a83d5c0326ace29c04e9133d41afa355fa606583817a27077d3f866001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa1580156107b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107db91908101906120b2565b6040516107e89190611d5f565b60405180910390a45050505050565b6108008161111b565b610811816314c4537960e21b6111d8565b8160005b818110156108475761083f8585838181106108325761083261215e565b90506020020135846115ff565b600101610815565b5050505050565b6001600160a01b03811660009081526007602052604081208190819061087490866115cb565b91509150816106635760405162461bcd60e51b8152602060048201526012602482015271139bc81b5a5b9d195c88185cdcda59db995960721b604482015260640161042e565b6108c38261111b565b6108d482632f4a71ef60e11b6111d8565b6001600160a01b03821660009081526006602052604090206108f690826116db565b6109125760405162461bcd60e51b815260040161042e90612174565b806001600160a01b0316826001600160a01b03167ff85412168568a1b1d7fda7902afcdb9b2b5380b30d67881d704a1c77cab63045836001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610985573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109ad91908101906120b2565b6040516109ba9190611d5f565b60405180910390a35050565b60606105e765076322e302e360d41b6112a9565b60405162461bcd60e51b8152602060048201526019602482015278043616e6e6f742072656e6f756e6365206f776e65727368697603c1b604482015260640161042e565b610a278161111b565b610a39828263765aabff60e01b611314565b610a4382826115ff565b5050565b610a57634258cf8f60e11b6116f0565b610a626004826116db565b610a7e5760405162461bcd60e51b815260040161042e90612174565b806001600160a01b03167f0d736cadcb29a6b8ae53b321e79e808a23cd1a48e960e4b566bcb2e9d6cccba8826001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610ae7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b0f91908101906120b2565b604051610b1c9190611d5f565b60405180910390a250565b6000546001600160a01b031690565b6000610b4182610b70565b80610b6957506001600160a01b0383166000908152600660205260409020610b699083611740565b9392505050565b60006105c5600483611740565b610b8d634a89cfb360e11b6116f0565b610b9681611755565b50565b6001600160a01b038083166000908152600760209081526040808320938516835260039091528120546060929190610bd990610bd4846117a8565b6117b3565b9050806001600160401b03811115610bf357610bf361209c565b604051908082528060200260200182016040528015610c1c578160200160208202803683370190505b5092506000610c2a836117a8565b90506000805b82811015610c9257600080610c4587846117c9565b91509150886001600160a01b0316816001600160a01b031603610c885781888580600101965081518110610c7b57610c7b61215e565b6020026020010181815250505b5050600101610c30565b5080831115610c9f578085525b5050505092915050565b6001600160a01b0381166000908152600660205260408120606091610ccd826117d8565b9050806001600160401b03811115610ce757610ce761209c565b604051908082528060200260200182016040528015610d2d57816020015b604080518082019091526000815260606020820152815260200190600190039081610d055790505b50925060005b81811015610df8576000610d4784836117e2565b90506000816001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610d89573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610db191908101906120b2565b90506040518060400160405280836001600160a01b0316815260200182815250868481518110610de357610de361215e565b60209081029190910101525050600101610d33565b505050919050565b60606000610e0e60046117d8565b9050806001600160401b03811115610e2857610e2861209c565b604051908082528060200260200182016040528015610e6e57816020015b604080518082019091526000815260606020820152815260200190600190039081610e465790505b50915060005b81811015610f3a576000610e896004836117e2565b90506000816001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610ecb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ef391908101906120b2565b90506040518060400160405280836001600160a01b0316815260200182815250858481518110610f2557610f2561215e565b60209081029190910101525050600101610e74565b505090565b6001600160a01b03821660009081526007602052604081208190606090610f6690856117c9565b8093508194505050816001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610fac573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610fd491908101906120b2565b90509250925092565b610fed633908325560e21b6116f0565b610ff8600482611294565b6110145760405162461bcd60e51b815260040161042e90611fd0565b6040516001600160a01b038216907f370244067623daed110e586dc655aa7544b64008c7bfebe6cc5d235933a3395890600090a250565b6001600160a01b0381166000908152600760205260408120819061106f90856115cb565b50949350505050565b6110806117ee565b6001600160a01b0381166110e55760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161042e565b610b968161184f565b6001600160a01b03811660009081526007602052604081206105c5906117a8565b6000610b69838361187a565b60025460405163f1052af560e01b81526001600160a01b039091169063f1052af59061114b908490600401611cde565b602060405180830381865afa158015611168573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118c9190612007565b610b965760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c79207265676973746572656420636f726520636f6e7472616374000000604482015260640161042e565b60405163230448b160e01b81526001600160a01b0383169063230448b19061120890339030908690600401612029565b6020604051808303816000875af1158015611227573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124b9190612007565b610a435760405162461bcd60e51b815260206004820152601a60248201527913db9b1e4810dbdc994810591b5a5b9050d308185b1b1bddd95960321b604482015260640161042e565b6000610b69836001600160a01b0384166118ea565b606060005b828160ff16602081106112c3576112c361215e565b1a60f81b6001600160f81b031916158015906112e2575060208160ff16105b156112f957806112f1816121a5565b9150506112ae565b60405191506040820160405280825282602083015250919050565b60405163a47d29cb60e01b81526004810184905282906001600160a01b0382169063a47d29cb90602401602060405180830381865afa15801561135b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137f91906121c4565b6001600160a01b0316336001600160a01b0316148061140c575060405163230448b160e01b81526001600160a01b0382169063230448b1906113c990339030908790600401612029565b6020604051808303816000875af11580156113e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140c9190612007565b6114585760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c7920417274697374206f7220436f72652041646d696e2041434c000000604482015260640161042e565b50505050565b6114688282610b36565b610a435760405162461bcd60e51b81526020600482015260156024820152744f6e6c7920617070726f766564206d696e7465727360581b604482015260640161042e565b6000819050806001600160a01b031663f893c07b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115139190611fb7565b83101580156115825750806001600160a01b031663e935b7b16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561155b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157f9190611fb7565b83105b6115c65760405162461bcd60e51b815260206004820152601560248201527413db9b1e481d985b1a59081c1c9bda9958dd081251605a1b604482015260640161042e565b505050565b60008080806115da86866119dd565b909450925050505b9250929050565b600061066384846001600160a01b038516611a17565b6040805180820182526012815271139bc81b5a5b9d195c88185cdcda59db995960721b6020808301919091526001600160a01b038416600090815260079091529182206003929161165291908690611a34565b6001600160a01b03168152602081019190915260400160009081208054916116798361206c565b90915550506001600160a01b03811660009081526007602052604090206116a09083611a41565b506040516001600160a01b0382169083907f08cccda053ef9dd4d5864c1af5303d9ef0277a79c21cacee3cd0b34dd4e9160590600090a35050565b6000610b69836001600160a01b038416611a4d565b6116fb3330836105ec565b610b965760405162461bcd60e51b815260206004820152601660248201527513db9b1e4810591b5a5b881050d308185b1b1bddd95960521b604482015260640161042e565b6000610b69836001600160a01b038416611a97565b61175e81611aaf565b600280546001600160a01b0319166001600160a01b0383169081179091556040517fb7b620c2c1cece07219b2ac19100c3f8629a9752c3c5484ed6524e99d0bddeed90600090a250565b60006105c582611afd565b60008183106117c25781610b69565b5090919050565b60008080806115da8686611b08565b60006105c5825490565b6000610b698383611b33565b336117f7610b27565b6001600160a01b03161461184d5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161042e565b565b61185881611b5d565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60008181526002830160205260408120548015158061189e575061189e8484611bad565b610b695760405162461bcd60e51b815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b65790000604482015260640161042e565b600081815260018301602052604081205480156119d357600061190e6001836121e1565b8554909150600090611922906001906121e1565b90508181146119875760008660000182815481106119425761194261215e565b90600052602060002001549050808760000184815481106119655761196561215e565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080611998576119986121f4565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105c5565b60009150506105c5565b6000818152600283016020526040812054819080611a0c576119ff8585611bad565b9250600091506115e29050565b6001925090506115e2565b600082815260028401602052604081208290556106638484611bb9565b6000610663848484611bc5565b6000610b698383611c08565b6000611a598383611a97565b611a8f575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105c5565b5060006105c5565b60009081526001919091016020526040902054151590565b6001600160a01b038116610b965760405162461bcd60e51b81526020600482015260156024820152744f6e6c79206e6f6e2d7a65726f206164647265737360581b604482015260640161042e565b60006105c5826117d8565b60008080611b1685856117e2565b600081815260029690960160205260409095205494959350505050565b6000826000018281548110611b4a57611b4a61215e565b9060005260206000200154905092915050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000610b698383611c25565b6000610b698383611a4d565b600082815260028401602052604081205480151580611be95750611be98585611bad565b839061106f5760405162461bcd60e51b815260040161042e9190611d5f565b60008181526002830160205260408120819055610b698383611c31565b6000610b698383611a97565b6000610b6983836118ea565b6001600160a01b0381168114610b9657600080fd5b60008060008060808587031215611c6857600080fd5b8435611c7381611c3d565b9350602085013592506040850135611c8a81611c3d565b91506060850135611c9a81611c3d565b939692955090935050565b60008060408385031215611cb857600080fd5b8235611cc381611c3d565b91506020830135611cd381611c3d565b809150509250929050565b6001600160a01b0391909116815260200190565b600060208284031215611d0457600080fd5b8135610b6981611c3d565b60005b83811015611d2a578181015183820152602001611d12565b50506000910152565b60008151808452611d4b816020860160208601611d0f565b601f01601f19169290920160200192915050565b602081526000610b696020830184611d33565b600080600060608486031215611d8757600080fd5b8335611d9281611c3d565b92506020840135611da281611c3d565b915060408401356001600160e01b031981168114611dbf57600080fd5b809150509250925092565b600080600060608486031215611ddf57600080fd5b833592506020840135611df181611c3d565b91506040840135611dbf81611c3d565b600080600060408486031215611e1657600080fd5b83356001600160401b0380821115611e2d57600080fd5b818601915086601f830112611e4157600080fd5b813581811115611e5057600080fd5b8760208260051b8501011115611e6557600080fd5b60209283019550935050840135611dbf81611c3d565b60008060408385031215611e8e57600080fd5b823591506020830135611cd381611c3d565b6020808252825182820181905260009190848201906040850190845b81811015611ed857835183529284019291840191600101611ebc565b50909695505050505050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015611f5357888303603f19018552815180516001600160a01b03168452870151878401879052611f4087850182611d33565b9588019593505090860190600101611f0b565b509098975050505050505050565b60008060408385031215611f7457600080fd5b8235611f7f81611c3d565b946020939093013593505050565b8381526001600160a01b03831660208201526060604082018190526000906104ae90830184611d33565b600060208284031215611fc957600080fd5b5051919050565b6020808252601f908201527f4f6e6c792070726576696f75736c7920617070726f766564206d696e74657200604082015260600190565b60006020828403121561201957600080fd5b81518015158114610b6957600080fd5b6001600160a01b0393841681529190921660208201526001600160e01b0319909116604082015260600190565b634e487b7160e01b600052601160045260246000fd5b60008161207b5761207b612056565b506000190190565b60006001820161209557612095612056565b5060010190565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156120c457600080fd5b81516001600160401b03808211156120db57600080fd5b818401915084601f8301126120ef57600080fd5b8151818111156121015761210161209c565b604051601f8201601f19908116603f011681019083821181831017156121295761212961209c565b8160405282815287602084870101111561214257600080fd5b612153836020830160208801611d0f565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b602080825260179082015276135a5b9d195c88185b1c9958591e48185c1c1c9bdd9959604a1b604082015260600190565b600060ff821660ff81036121bb576121bb612056565b60010192915050565b6000602082840312156121d657600080fd5b8151610b6981611c3d565b818103818111156105c5576105c5612056565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220d086cd837f4af5b72d1752ec2b8754222402401d43a56569df878a1e5335b0f964736f6c63430008130033000000000000000000000000fafda82cde1502c41e597e4656953533c16ca9940000000000000000000000002ee7b9bb2e038be7323a119701a191c030a61ec6

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101625760003560e01c8063765aabff116100c9578063765aabff1461029c57806384b19f1e146102af5780638da5cb5b146102c25780638e5b9056146102ca5780638f48e2c0146102dd57806395139f66146102f0578063969c3ed714610303578063a02b095214610323578063a365254714610343578063af439a311461034b578063e420c9541461036d578063ea6e376114610380578063f2fde38b14610393578063fd9ca435146103a657600080fd5b80615f34146101675780630a3eecb21461018d5780631e9bef46146101a25780631f8608c6146101c257806320f2ccb7146101e5578063230448b1146101fa57806326df17b51461020d57806341946f0f1461022d57806353114de4146102405780635845de1f146102535780635e94e3de146102665780636bf3d05a146102795780637136b93e1461028c578063715018a614610294575b600080fd5b61017a610175366004611c52565b6103b9565b6040519081526020015b60405180910390f35b6101a061019b366004611ca5565b6104b7565b005b6001546101b5906001600160a01b031681565b6040516101849190611cde565b6101d56101d0366004611cf2565b610553565b6040519015158152602001610184565b6101ed6105cb565b6040516101849190611d5f565b6101d5610208366004611d72565b6105ec565b61017a61021b366004611cf2565b60036020526000908152604090205481565b6101a061023b366004611dca565b61066b565b6101a061024e366004611e01565b6107f7565b6101b5610261366004611e7b565b61084e565b6101a0610274366004611ca5565b6108ba565b6002546101b5906001600160a01b031681565b6101ed6109c6565b6101a06109da565b6101a06102aa366004611e7b565b610a1e565b6101a06102bd366004611cf2565b610a47565b6101b5610b27565b6101d56102d8366004611ca5565b610b36565b6101d56102eb366004611cf2565b610b70565b6101a06102fe366004611cf2565b610b7d565b610316610311366004611ca5565b610b99565b6040516101849190611ea0565b610336610331366004611cf2565b610ca9565b6040516101849190611ee4565b610336610e00565b61035e610359366004611f61565b610f3f565b60405161018493929190611f8d565b6101a061037b366004611cf2565b610fdd565b6101d561038e366004611e7b565b61104b565b6101a06103a1366004611cf2565b611078565b61017a6103b4366004611cf2565b6110ee565b6001600160a01b03821660009081526007602052604081206103db908561110f565b6001600160a01b0316336001600160a01b0316146104375760405162461bcd60e51b815260206004820152601460248201527327b7363c9030b9b9b4b3b732b21036b4b73a32b960611b60448201526064015b60405180910390fd5b604051615de560e01b81526001600160a01b038681166004830152602482018690528381166044830152841690615de5906064016020604051808303816000875af115801561048a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104ae9190611fb7565b95945050505050565b6104c08261111b565b6104d18263051f765960e11b6111d8565b6001600160a01b03821660009081526006602052604090206104f39082611294565b61050f5760405162461bcd60e51b815260040161042e90611fd0565b806001600160a01b0316826001600160a01b03167f60ecff7c83062c43d6aae585dca525293afec7a4f4c378d12927ce8f7525da2460405160405180910390a35050565b60025460405163f1052af560e01b81526000916001600160a01b03169063f1052af590610584908590600401611cde565b602060405180830381865afa1580156105a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c59190612007565b92915050565b60606105e76d26b4b73a32b92334b63a32b92b1960911b6112a9565b905090565b6001546040516217798b60e61b81526000916001600160a01b0316906305de62c09061062090879087908790600401612029565b6020604051808303816000875af115801561063f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106639190612007565b949350505050565b6106748261111b565b61068683836341946f0f60e01b611314565b610690828261145e565b61069a83836114ac565b6001600160a01b038216600090815260076020526040812081906106be90866115cb565b9150915081156106f2576001600160a01b03811660009081526003602052604081208054916106ec8361206c565b91905055505b6001600160a01b038316600090815260036020526040812080549161071683612083565b90915550506001600160a01b038416600090815260076020526040902061073e9086856115e9565b50826001600160a01b0316846001600160a01b0316867ff012149282a4a83d5c0326ace29c04e9133d41afa355fa606583817a27077d3f866001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa1580156107b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107db91908101906120b2565b6040516107e89190611d5f565b60405180910390a45050505050565b6108008161111b565b610811816314c4537960e21b6111d8565b8160005b818110156108475761083f8585838181106108325761083261215e565b90506020020135846115ff565b600101610815565b5050505050565b6001600160a01b03811660009081526007602052604081208190819061087490866115cb565b91509150816106635760405162461bcd60e51b8152602060048201526012602482015271139bc81b5a5b9d195c88185cdcda59db995960721b604482015260640161042e565b6108c38261111b565b6108d482632f4a71ef60e11b6111d8565b6001600160a01b03821660009081526006602052604090206108f690826116db565b6109125760405162461bcd60e51b815260040161042e90612174565b806001600160a01b0316826001600160a01b03167ff85412168568a1b1d7fda7902afcdb9b2b5380b30d67881d704a1c77cab63045836001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610985573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109ad91908101906120b2565b6040516109ba9190611d5f565b60405180910390a35050565b60606105e765076322e302e360d41b6112a9565b60405162461bcd60e51b8152602060048201526019602482015278043616e6e6f742072656e6f756e6365206f776e65727368697603c1b604482015260640161042e565b610a278161111b565b610a39828263765aabff60e01b611314565b610a4382826115ff565b5050565b610a57634258cf8f60e11b6116f0565b610a626004826116db565b610a7e5760405162461bcd60e51b815260040161042e90612174565b806001600160a01b03167f0d736cadcb29a6b8ae53b321e79e808a23cd1a48e960e4b566bcb2e9d6cccba8826001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610ae7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b0f91908101906120b2565b604051610b1c9190611d5f565b60405180910390a250565b6000546001600160a01b031690565b6000610b4182610b70565b80610b6957506001600160a01b0383166000908152600660205260409020610b699083611740565b9392505050565b60006105c5600483611740565b610b8d634a89cfb360e11b6116f0565b610b9681611755565b50565b6001600160a01b038083166000908152600760209081526040808320938516835260039091528120546060929190610bd990610bd4846117a8565b6117b3565b9050806001600160401b03811115610bf357610bf361209c565b604051908082528060200260200182016040528015610c1c578160200160208202803683370190505b5092506000610c2a836117a8565b90506000805b82811015610c9257600080610c4587846117c9565b91509150886001600160a01b0316816001600160a01b031603610c885781888580600101965081518110610c7b57610c7b61215e565b6020026020010181815250505b5050600101610c30565b5080831115610c9f578085525b5050505092915050565b6001600160a01b0381166000908152600660205260408120606091610ccd826117d8565b9050806001600160401b03811115610ce757610ce761209c565b604051908082528060200260200182016040528015610d2d57816020015b604080518082019091526000815260606020820152815260200190600190039081610d055790505b50925060005b81811015610df8576000610d4784836117e2565b90506000816001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610d89573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610db191908101906120b2565b90506040518060400160405280836001600160a01b0316815260200182815250868481518110610de357610de361215e565b60209081029190910101525050600101610d33565b505050919050565b60606000610e0e60046117d8565b9050806001600160401b03811115610e2857610e2861209c565b604051908082528060200260200182016040528015610e6e57816020015b604080518082019091526000815260606020820152815260200190600190039081610e465790505b50915060005b81811015610f3a576000610e896004836117e2565b90506000816001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610ecb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ef391908101906120b2565b90506040518060400160405280836001600160a01b0316815260200182815250858481518110610f2557610f2561215e565b60209081029190910101525050600101610e74565b505090565b6001600160a01b03821660009081526007602052604081208190606090610f6690856117c9565b8093508194505050816001600160a01b031663e9d1e8ac6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610fac573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610fd491908101906120b2565b90509250925092565b610fed633908325560e21b6116f0565b610ff8600482611294565b6110145760405162461bcd60e51b815260040161042e90611fd0565b6040516001600160a01b038216907f370244067623daed110e586dc655aa7544b64008c7bfebe6cc5d235933a3395890600090a250565b6001600160a01b0381166000908152600760205260408120819061106f90856115cb565b50949350505050565b6110806117ee565b6001600160a01b0381166110e55760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161042e565b610b968161184f565b6001600160a01b03811660009081526007602052604081206105c5906117a8565b6000610b69838361187a565b60025460405163f1052af560e01b81526001600160a01b039091169063f1052af59061114b908490600401611cde565b602060405180830381865afa158015611168573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118c9190612007565b610b965760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c79207265676973746572656420636f726520636f6e7472616374000000604482015260640161042e565b60405163230448b160e01b81526001600160a01b0383169063230448b19061120890339030908690600401612029565b6020604051808303816000875af1158015611227573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124b9190612007565b610a435760405162461bcd60e51b815260206004820152601a60248201527913db9b1e4810dbdc994810591b5a5b9050d308185b1b1bddd95960321b604482015260640161042e565b6000610b69836001600160a01b0384166118ea565b606060005b828160ff16602081106112c3576112c361215e565b1a60f81b6001600160f81b031916158015906112e2575060208160ff16105b156112f957806112f1816121a5565b9150506112ae565b60405191506040820160405280825282602083015250919050565b60405163a47d29cb60e01b81526004810184905282906001600160a01b0382169063a47d29cb90602401602060405180830381865afa15801561135b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137f91906121c4565b6001600160a01b0316336001600160a01b0316148061140c575060405163230448b160e01b81526001600160a01b0382169063230448b1906113c990339030908790600401612029565b6020604051808303816000875af11580156113e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140c9190612007565b6114585760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c7920417274697374206f7220436f72652041646d696e2041434c000000604482015260640161042e565b50505050565b6114688282610b36565b610a435760405162461bcd60e51b81526020600482015260156024820152744f6e6c7920617070726f766564206d696e7465727360581b604482015260640161042e565b6000819050806001600160a01b031663f893c07b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115139190611fb7565b83101580156115825750806001600160a01b031663e935b7b16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561155b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157f9190611fb7565b83105b6115c65760405162461bcd60e51b815260206004820152601560248201527413db9b1e481d985b1a59081c1c9bda9958dd081251605a1b604482015260640161042e565b505050565b60008080806115da86866119dd565b909450925050505b9250929050565b600061066384846001600160a01b038516611a17565b6040805180820182526012815271139bc81b5a5b9d195c88185cdcda59db995960721b6020808301919091526001600160a01b038416600090815260079091529182206003929161165291908690611a34565b6001600160a01b03168152602081019190915260400160009081208054916116798361206c565b90915550506001600160a01b03811660009081526007602052604090206116a09083611a41565b506040516001600160a01b0382169083907f08cccda053ef9dd4d5864c1af5303d9ef0277a79c21cacee3cd0b34dd4e9160590600090a35050565b6000610b69836001600160a01b038416611a4d565b6116fb3330836105ec565b610b965760405162461bcd60e51b815260206004820152601660248201527513db9b1e4810591b5a5b881050d308185b1b1bddd95960521b604482015260640161042e565b6000610b69836001600160a01b038416611a97565b61175e81611aaf565b600280546001600160a01b0319166001600160a01b0383169081179091556040517fb7b620c2c1cece07219b2ac19100c3f8629a9752c3c5484ed6524e99d0bddeed90600090a250565b60006105c582611afd565b60008183106117c25781610b69565b5090919050565b60008080806115da8686611b08565b60006105c5825490565b6000610b698383611b33565b336117f7610b27565b6001600160a01b03161461184d5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161042e565b565b61185881611b5d565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60008181526002830160205260408120548015158061189e575061189e8484611bad565b610b695760405162461bcd60e51b815260206004820152601e60248201527f456e756d657261626c654d61703a206e6f6e6578697374656e74206b65790000604482015260640161042e565b600081815260018301602052604081205480156119d357600061190e6001836121e1565b8554909150600090611922906001906121e1565b90508181146119875760008660000182815481106119425761194261215e565b90600052602060002001549050808760000184815481106119655761196561215e565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080611998576119986121f4565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105c5565b60009150506105c5565b6000818152600283016020526040812054819080611a0c576119ff8585611bad565b9250600091506115e29050565b6001925090506115e2565b600082815260028401602052604081208290556106638484611bb9565b6000610663848484611bc5565b6000610b698383611c08565b6000611a598383611a97565b611a8f575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105c5565b5060006105c5565b60009081526001919091016020526040902054151590565b6001600160a01b038116610b965760405162461bcd60e51b81526020600482015260156024820152744f6e6c79206e6f6e2d7a65726f206164647265737360581b604482015260640161042e565b60006105c5826117d8565b60008080611b1685856117e2565b600081815260029690960160205260409095205494959350505050565b6000826000018281548110611b4a57611b4a61215e565b9060005260206000200154905092915050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000610b698383611c25565b6000610b698383611a4d565b600082815260028401602052604081205480151580611be95750611be98585611bad565b839061106f5760405162461bcd60e51b815260040161042e9190611d5f565b60008181526002830160205260408120819055610b698383611c31565b6000610b698383611a97565b6000610b6983836118ea565b6001600160a01b0381168114610b9657600080fd5b60008060008060808587031215611c6857600080fd5b8435611c7381611c3d565b9350602085013592506040850135611c8a81611c3d565b91506060850135611c9a81611c3d565b939692955090935050565b60008060408385031215611cb857600080fd5b8235611cc381611c3d565b91506020830135611cd381611c3d565b809150509250929050565b6001600160a01b0391909116815260200190565b600060208284031215611d0457600080fd5b8135610b6981611c3d565b60005b83811015611d2a578181015183820152602001611d12565b50506000910152565b60008151808452611d4b816020860160208601611d0f565b601f01601f19169290920160200192915050565b602081526000610b696020830184611d33565b600080600060608486031215611d8757600080fd5b8335611d9281611c3d565b92506020840135611da281611c3d565b915060408401356001600160e01b031981168114611dbf57600080fd5b809150509250925092565b600080600060608486031215611ddf57600080fd5b833592506020840135611df181611c3d565b91506040840135611dbf81611c3d565b600080600060408486031215611e1657600080fd5b83356001600160401b0380821115611e2d57600080fd5b818601915086601f830112611e4157600080fd5b813581811115611e5057600080fd5b8760208260051b8501011115611e6557600080fd5b60209283019550935050840135611dbf81611c3d565b60008060408385031215611e8e57600080fd5b823591506020830135611cd381611c3d565b6020808252825182820181905260009190848201906040850190845b81811015611ed857835183529284019291840191600101611ebc565b50909695505050505050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015611f5357888303603f19018552815180516001600160a01b03168452870151878401879052611f4087850182611d33565b9588019593505090860190600101611f0b565b509098975050505050505050565b60008060408385031215611f7457600080fd5b8235611f7f81611c3d565b946020939093013593505050565b8381526001600160a01b03831660208201526060604082018190526000906104ae90830184611d33565b600060208284031215611fc957600080fd5b5051919050565b6020808252601f908201527f4f6e6c792070726576696f75736c7920617070726f766564206d696e74657200604082015260600190565b60006020828403121561201957600080fd5b81518015158114610b6957600080fd5b6001600160a01b0393841681529190921660208201526001600160e01b0319909116604082015260600190565b634e487b7160e01b600052601160045260246000fd5b60008161207b5761207b612056565b506000190190565b60006001820161209557612095612056565b5060010190565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156120c457600080fd5b81516001600160401b03808211156120db57600080fd5b818401915084601f8301126120ef57600080fd5b8151818111156121015761210161209c565b604051601f8201601f19908116603f011681019083821181831017156121295761212961209c565b8160405282815287602084870101111561214257600080fd5b612153836020830160208801611d0f565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b602080825260179082015276135a5b9d195c88185b1c9958591e48185c1c1c9bdd9959604a1b604082015260600190565b600060ff821660ff81036121bb576121bb612056565b60010192915050565b6000602082840312156121d657600080fd5b8151610b6981611c3d565b818103818111156105c5576105c5612056565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220d086cd837f4af5b72d1752ec2b8754222402401d43a56569df878a1e5335b0f964736f6c63430008130033

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

000000000000000000000000fafda82cde1502c41e597e4656953533c16ca9940000000000000000000000002ee7b9bb2e038be7323a119701a191c030a61ec6

-----Decoded View---------------
Arg [0] : adminACLContract_ (address): 0xfaFda82CDe1502c41e597e4656953533c16ca994
Arg [1] : coreRegistry_ (address): 0x2eE7B9bB2E038bE7323A119701A191c030A61ec6

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000fafda82cde1502c41e597e4656953533c16ca994
Arg [1] : 0000000000000000000000002ee7b9bb2e038be7323a119701a191c030a61ec6


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.