ETH Price: $3,144.04 (-8.52%)
Gas: 10 Gwei

Contract

0x4610dB225D7305e31690658D674E7D37eB244f7e
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Purchase To167008972023-02-24 21:37:23516 days ago1677274643IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0036878623.99888268
Purchase To167008912023-02-24 21:36:11516 days ago1677274571IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0037409524.79703318
Purchase To167008862023-02-24 21:35:11516 days ago1677274511IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0037110124.60020683
Purchase To167008822023-02-24 21:34:23516 days ago1677274463IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0039805226.38505171
Purchase To167008782023-02-24 21:33:35516 days ago1677274415IN
0x4610dB22...7eB244f7e
2.0476 ETH0.004225327.97491743
Purchase To167008732023-02-24 21:32:35516 days ago1677274355IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0043482628.85818961
Purchase To167008682023-02-24 21:31:35516 days ago1677274295IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0045347130.06446097
Purchase To167008622023-02-24 21:30:23516 days ago1677274223IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0041352327.3768004
Purchase To167008562023-02-24 21:29:11516 days ago1677274151IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0041582827.53300229
Purchase To167008512023-02-24 21:28:11516 days ago1677274091IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0042469328.1912965
Purchase To167008462023-02-24 21:27:11516 days ago1677274031IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0043015228.54796866
Purchase To167008412023-02-24 21:26:11516 days ago1677273971IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0045048829.82797271
Purchase To167008362023-02-24 21:24:59516 days ago1677273899IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0045957230.4629343
Purchase To167008302023-02-24 21:23:47516 days ago1677273827IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0046914431.10357299
Purchase To167008242023-02-24 21:22:35516 days ago1677273755IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0048870332.44028688
Purchase To167008182023-02-24 21:21:23516 days ago1677273683IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0048575932.20085646
Purchase To167008112023-02-24 21:19:59516 days ago1677273599IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0043103828.57718503
Purchase To167008042023-02-24 21:18:23516 days ago1677273503IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0044268329.34343147
Purchase To167007982023-02-24 21:17:11516 days ago1677273431IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0039927226.43680085
Purchase To167007932023-02-24 21:16:11516 days ago1677273371IN
0x4610dB22...7eB244f7e
2.0476 ETH0.0045743227.26430407
Update Merkle Ro...167007802023-02-24 21:13:35516 days ago1677273215IN
0x4610dB22...7eB244f7e
0 ETH0.0008862625.4432986
Update Merkle Ro...167007392023-02-24 21:05:11516 days ago1677272711IN
0x4610dB22...7eB244f7e
0 ETH0.0008926525.62677699
Set Project Invo...167007172023-02-24 21:00:35516 days ago1677272435IN
0x4610dB22...7eB244f7e
0 ETH0.0009997127.68375863
Manually Limit P...167007002023-02-24 20:56:59516 days ago1677272219IN
0x4610dB22...7eB244f7e
0 ETH0.000978924.3036108
Purchase To167006262023-02-24 20:41:59516 days ago1677271319IN
0x4610dB22...7eB244f7e
2.0476 ETH0.005190826.63782132
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
167008972023-02-24 21:37:23516 days ago1677274643
0x4610dB22...7eB244f7e
0.552852 ETH
167008972023-02-24 21:37:23516 days ago1677274643
0x4610dB22...7eB244f7e
1.289988 ETH
167008972023-02-24 21:37:23516 days ago1677274643
0x4610dB22...7eB244f7e
0.20476 ETH
167008912023-02-24 21:36:11516 days ago1677274571
0x4610dB22...7eB244f7e
0.552852 ETH
167008912023-02-24 21:36:11516 days ago1677274571
0x4610dB22...7eB244f7e
1.289988 ETH
167008912023-02-24 21:36:11516 days ago1677274571
0x4610dB22...7eB244f7e
0.20476 ETH
167008862023-02-24 21:35:11516 days ago1677274511
0x4610dB22...7eB244f7e
0.552852 ETH
167008862023-02-24 21:35:11516 days ago1677274511
0x4610dB22...7eB244f7e
1.289988 ETH
167008862023-02-24 21:35:11516 days ago1677274511
0x4610dB22...7eB244f7e
0.20476 ETH
167008822023-02-24 21:34:23516 days ago1677274463
0x4610dB22...7eB244f7e
0.552852 ETH
167008822023-02-24 21:34:23516 days ago1677274463
0x4610dB22...7eB244f7e
1.289988 ETH
167008822023-02-24 21:34:23516 days ago1677274463
0x4610dB22...7eB244f7e
0.20476 ETH
167008782023-02-24 21:33:35516 days ago1677274415
0x4610dB22...7eB244f7e
0.552852 ETH
167008782023-02-24 21:33:35516 days ago1677274415
0x4610dB22...7eB244f7e
1.289988 ETH
167008782023-02-24 21:33:35516 days ago1677274415
0x4610dB22...7eB244f7e
0.20476 ETH
167008732023-02-24 21:32:35516 days ago1677274355
0x4610dB22...7eB244f7e
0.552852 ETH
167008732023-02-24 21:32:35516 days ago1677274355
0x4610dB22...7eB244f7e
1.289988 ETH
167008732023-02-24 21:32:35516 days ago1677274355
0x4610dB22...7eB244f7e
0.20476 ETH
167008682023-02-24 21:31:35516 days ago1677274295
0x4610dB22...7eB244f7e
0.552852 ETH
167008682023-02-24 21:31:35516 days ago1677274295
0x4610dB22...7eB244f7e
1.289988 ETH
167008682023-02-24 21:31:35516 days ago1677274295
0x4610dB22...7eB244f7e
0.20476 ETH
167008622023-02-24 21:30:23516 days ago1677274223
0x4610dB22...7eB244f7e
0.552852 ETH
167008622023-02-24 21:30:23516 days ago1677274223
0x4610dB22...7eB244f7e
1.289988 ETH
167008622023-02-24 21:30:23516 days ago1677274223
0x4610dB22...7eB244f7e
0.20476 ETH
167008562023-02-24 21:29:11516 days ago1677274151
0x4610dB22...7eB244f7e
0.552852 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MinterMerkleV5

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 25 runs

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

import "../../interfaces/0.8.x/IGenArt721CoreContractV3_Base.sol";
import "../../interfaces/0.8.x/IMinterFilterV0.sol";
import "../../interfaces/0.8.x/IFilteredMinterMerkleV2.sol";
import "../../interfaces/0.8.x/IDelegationRegistry.sol";
import "./MinterBase_v0_1_1.sol";

import "@openzeppelin-4.7/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin-4.7/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin-4.7/contracts/security/ReentrancyGuard.sol";

pragma solidity 0.8.17;

/**
 * @title Filtered Minter contract that allows tokens to be minted with ETH
 * for addresses in a Merkle allowlist.
 * This is designed to be used with GenArt721CoreContractV3 flagship or
 * engine contracts.
 * @author Art Blocks Inc.
 * @notice Privileged Roles and Ownership:
 * This contract is designed to be managed, with limited powers.
 * Privileged roles and abilities are controlled by the project's artist, which
 * can be modified by the core contract's Admin ACL contract. Both of these
 * roles hold extensive power and can modify minter details.
 * 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 to a project's artist:
 * - updateMerkleRoot
 * - updatePricePerTokenInWei
 * - setProjectInvocationsPerAddress
 * - setProjectMaxInvocations
 * - manuallyLimitProjectMaxInvocations
 * ----------------------------------------------------------------------------
 * Additional admin and artist privileged roles may be described on other
 * contracts that this minter integrates with.
 * ----------------------------------------------------------------------------
 * This contract allows vaults to configure token-level or wallet-level
 * delegation of minting privileges. This allows a vault on an allowlist to
 * delegate minting privileges to a wallet that is not on the allowlist,
 * enabling the vault to remain air-gapped while still allowing minting. The
 * delegation registry contract is responsible for managing these delegations,
 * and is available at the address returned by the public immutable
 * `delegationRegistryAddress`. At the time of writing, the delegation
 * registry enables easy delegation configuring at https://delegate.cash/.
 * Art Blocks does not guarentee the security of the delegation registry, and
 * users should take care to ensure that the delegation registry is secure.
 * Token-level delegations are configured by the vault owner, and contract-
 * level delegations must be configured for the core token contract as returned
 * by the public immutable variable `genArt721CoreAddress`.
 */
contract MinterMerkleV5 is
    ReentrancyGuard,
    MinterBase,
    IFilteredMinterMerkleV2
{
    using MerkleProof for bytes32[];

    /// Delegation registry address
    address public immutable delegationRegistryAddress;

    /// Delegation registry address
    IDelegationRegistry private immutable delegationRegistryContract;

    /// Core contract address this minter interacts with
    address public immutable genArt721CoreAddress;

    /// The core contract integrates with V3 contracts
    IGenArt721CoreContractV3_Base private immutable genArtCoreContract_Base;

    /// Minter filter address this minter interacts with
    address public immutable minterFilterAddress;

    /// Minter filter this minter may interact with.
    IMinterFilterV0 private immutable minterFilter;

    /// minterType for this minter
    string public constant minterType = "MinterMerkleV5";

    /// project minter configuration keys used by this minter
    bytes32 private constant CONFIG_MERKLE_ROOT = "merkleRoot";
    bytes32 private constant CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE =
        "useMaxMintsPerAddrOverride"; // shortened to fit in 32 bytes
    bytes32 private constant CONFIG_MAX_INVOCATIONS_OVERRIDE =
        "maxMintsPerAddrOverride"; // shortened to match format of previous key

    uint256 constant ONE_MILLION = 1_000_000;

    uint256 public constant DEFAULT_MAX_INVOCATIONS_PER_ADDRESS = 1;

    struct ProjectConfig {
        bool maxHasBeenInvoked;
        bool priceIsConfigured;
        // initial value is false, so by default, projects limit allowlisted
        // addresses to a mint qty of `DEFAULT_MAX_INVOCATIONS_PER_ADDRESS`
        bool useMaxInvocationsPerAddressOverride;
        // a value of 0 means no limit
        // (only used if `useMaxInvocationsPerAddressOverride` is true)
        uint24 maxInvocationsPerAddressOverride;
        uint24 maxInvocations;
        uint256 pricePerTokenInWei;
    }

    mapping(uint256 => ProjectConfig) public projectConfig;

    /// projectId => merkle root
    mapping(uint256 => bytes32) public projectMerkleRoot;

    /// projectId => purchaser address => qty of mints purchased for project
    mapping(uint256 => mapping(address => uint256))
        public projectUserMintInvocations;

    modifier onlyArtist(uint256 _projectId) {
        require(
            msg.sender ==
                genArtCoreContract_Base.projectIdToArtistAddress(_projectId),
            "Only Artist"
        );
        _;
    }

    /**
     * @notice Initializes contract to be a Filtered Minter for
     * `_minterFilter`, integrated with Art Blocks core contract
     * at address `_genArt721Address`. Also configures the delegation
     * registry address to be address `_delegationRegistryAddress`.
     * @param _genArt721Address Art Blocks core contract address for
     * which this contract will be a minter.
     * @param _minterFilter Minter filter for which this will be a
     * filtered minter.
     * @param _delegationRegistryAddress Delegation registry contract address.
     */
    constructor(
        address _genArt721Address,
        address _minterFilter,
        address _delegationRegistryAddress
    ) ReentrancyGuard() MinterBase(_genArt721Address) {
        genArt721CoreAddress = _genArt721Address;
        // always populate immutable engine contracts, but only use appropriate
        // interface based on isEngine in the rest of the contract
        genArtCoreContract_Base = IGenArt721CoreContractV3_Base(
            _genArt721Address
        );
        delegationRegistryAddress = _delegationRegistryAddress;
        emit DelegationRegistryUpdated(_delegationRegistryAddress);
        delegationRegistryContract = IDelegationRegistry(
            _delegationRegistryAddress
        );
        minterFilterAddress = _minterFilter;
        minterFilter = IMinterFilterV0(_minterFilter);
        require(
            minterFilter.genArt721CoreAddress() == _genArt721Address,
            "Illegal contract pairing"
        );
        // broadcast default max invocations per address for this minter
        emit DefaultMaxInvocationsPerAddress(
            DEFAULT_MAX_INVOCATIONS_PER_ADDRESS
        );
    }

    /**
     * @notice Update the Merkle root for project `_projectId`.
     * @param _projectId Project ID to be updated.
     * @param _root root of Merkle tree defining addresses allowed to mint
     * on project `_projectId`.
     */
    function updateMerkleRoot(
        uint256 _projectId,
        bytes32 _root
    ) external onlyArtist(_projectId) {
        require(_root != bytes32(0), "Root must be provided");
        projectMerkleRoot[_projectId] = _root;
        emit ConfigValueSet(_projectId, CONFIG_MERKLE_ROOT, _root);
    }

    /**
     * @notice Returns hashed address (to be used as merkle tree leaf).
     * Included as a public function to enable users to calculate their hashed
     * address in Solidity when generating proofs off-chain.
     * @param _address address to be hashed
     * @return bytes32 hashed address, via keccak256 (using encodePacked)
     */
    function hashAddress(address _address) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(_address));
    }

    /**
     * @notice Verify if address is allowed to mint on project `_projectId`.
     * @param _projectId Project ID to be checked.
     * @param _proof Merkle proof for address.
     * @param _address Address to check.
     * @return inAllowlist true only if address is allowed to mint and valid
     * Merkle proof was provided
     */
    function verifyAddress(
        uint256 _projectId,
        bytes32[] calldata _proof,
        address _address
    ) public view returns (bool) {
        return
            _proof.verifyCalldata(
                projectMerkleRoot[_projectId],
                hashAddress(_address)
            );
    }

    /**
     * @notice Sets maximum allowed invocations per allowlisted address for
     * project `_project` to `limit`. If `limit` is set to 0, allowlisted
     * addresses will be able to mint as many times as desired, until the
     * project reaches its maximum invocations.
     * Default is a value of 1 if never configured by artist.
     * @param _projectId Project ID to toggle the mint limit.
     * @param _maxInvocationsPerAddress Maximum allowed invocations per
     * allowlisted address.
     * @dev default value stated above must be updated if the value of
     * CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE is changed.
     */
    function setProjectInvocationsPerAddress(
        uint256 _projectId,
        uint24 _maxInvocationsPerAddress
    ) external onlyArtist(_projectId) {
        ProjectConfig storage _projectConfig = projectConfig[_projectId];
        // use override value instead of the contract's default
        // @dev this never changes from true to false; default value is only
        // used if artist has never configured project invocations per address
        _projectConfig.useMaxInvocationsPerAddressOverride = true;
        // update the override value
        _projectConfig
            .maxInvocationsPerAddressOverride = _maxInvocationsPerAddress;
        // generic events
        emit ConfigValueSet(
            _projectId,
            CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE,
            true
        );
        emit ConfigValueSet(
            _projectId,
            CONFIG_MAX_INVOCATIONS_OVERRIDE,
            uint256(_maxInvocationsPerAddress)
        );
    }

    /**
     * @notice Syncs local maximum invocations of project `_projectId` based on
     * the value currently defined in the core contract.
     * @param _projectId Project ID to set the maximum invocations for.
     * @dev this enables gas reduction after maxInvocations have been reached -
     * core contracts shall still enforce a maxInvocation check during mint.
     */
    function setProjectMaxInvocations(
        uint256 _projectId
    ) external onlyArtist(_projectId) {
        uint256 maxInvocations;
        uint256 invocations;
        (invocations, maxInvocations, , , , ) = genArtCoreContract_Base
            .projectStateData(_projectId);
        // update storage with results
        projectConfig[_projectId].maxInvocations = uint24(maxInvocations);

        // We need to ensure maxHasBeenInvoked is correctly set after manually syncing the
        // local maxInvocations value with the core contract's maxInvocations value.
        // This synced value of maxInvocations from the core contract will always be greater
        // than or equal to the previous value of maxInvocations stored locally.
        projectConfig[_projectId].maxHasBeenInvoked =
            invocations == maxInvocations;

        emit ProjectMaxInvocationsLimitUpdated(_projectId, maxInvocations);
    }

    /**
     * @notice Manually sets the local maximum invocations of project `_projectId`
     * with the provided `_maxInvocations`, checking that `_maxInvocations` is less
     * than or equal to the value of project `_project_id`'s maximum invocations that is
     * set on the core contract.
     * @dev Note that a `_maxInvocations` of 0 can only be set if the current `invocations`
     * value is also 0 and this would also set `maxHasBeenInvoked` to true, correctly short-circuiting
     * this minter's purchase function, avoiding extra gas costs from the core contract's maxInvocations check.
     * @param _projectId Project ID to set the maximum invocations for.
     * @param _maxInvocations Maximum invocations to set for the project.
     */
    function manuallyLimitProjectMaxInvocations(
        uint256 _projectId,
        uint256 _maxInvocations
    ) external onlyArtist(_projectId) {
        // CHECKS
        // ensure that the manually set maxInvocations is not greater than what is set on the core contract
        uint256 maxInvocations;
        uint256 invocations;
        (invocations, maxInvocations, , , , ) = genArtCoreContract_Base
            .projectStateData(_projectId);
        require(
            _maxInvocations <= maxInvocations,
            "Cannot increase project max invocations above core contract set project max invocations"
        );
        require(
            _maxInvocations >= invocations,
            "Cannot set project max invocations to less than current invocations"
        );

        // EFFECTS
        // update storage with results
        projectConfig[_projectId].maxInvocations = uint24(_maxInvocations);
        // We need to ensure maxHasBeenInvoked is correctly set after manually setting the
        // local maxInvocations value.
        projectConfig[_projectId].maxHasBeenInvoked =
            invocations == _maxInvocations;

        emit ProjectMaxInvocationsLimitUpdated(_projectId, _maxInvocations);
    }

    /**
     * @notice Warning: Disabling purchaseTo is not supported on this minter.
     * This method exists purely for interface-conformance purposes.
     */
    function togglePurchaseToDisabled(
        uint256 _projectId
    ) external view onlyArtist(_projectId) {
        revert("Action not supported");
    }

    /**
     * @notice projectId => has project reached its maximum number of
     * invocations? Note that this returns a local cache of the core contract's
     * state, and may be out of sync with the core contract. This is
     * intentional, as it only enables gas optimization of mints after a
     * project's maximum invocations has been reached. A false negative will
     * only result in a gas cost increase, since the core contract will still
     * enforce a maxInvocation check during minting. A false positive is not
     * possible because the V3 core contract only allows maximum invocations
     * to be reduced, not increased. Based on this rationale, we intentionally
     * do not do input validation in this method as to whether or not the input
     * `_projectId` is an existing project ID.
     */
    function projectMaxHasBeenInvoked(
        uint256 _projectId
    ) external view returns (bool) {
        return projectConfig[_projectId].maxHasBeenInvoked;
    }

    /**
     * @notice projectId => project's maximum number of invocations.
     * Optionally synced with core contract value, for gas optimization.
     * Note that this returns a local cache of the core contract's
     * state, and may be out of sync with the core contract. This is
     * intentional, as it only enables gas optimization of mints after a
     * project's maximum invocations has been reached.
     * @dev A number greater than the core contract's project max invocations
     * will only result in a gas cost increase, since the core contract will
     * still enforce a maxInvocation check during minting. A number less than
     * the core contract's project max invocations is only possible when the
     * project's max invocations have not been synced on this minter, since the
     * V3 core contract only allows maximum invocations to be reduced, not
     * increased. When this happens, the minter will enable minting, allowing
     * the core contract to enforce the max invocations check. Based on this
     * rationale, we intentionally do not do input validation in this method as
     * to whether or not the input `_projectId` is an existing project ID.
     */
    function projectMaxInvocations(
        uint256 _projectId
    ) external view returns (uint256) {
        return uint256(projectConfig[_projectId].maxInvocations);
    }

    /**
     * @notice Updates this minter's price per token of project `_projectId`
     * to be '_pricePerTokenInWei`, in Wei.
     * This price supersedes any legacy core contract price per token value.
     * @dev Note that it is intentionally supported here that the configured
     * price may be explicitly set to `0`.
     */
    function updatePricePerTokenInWei(
        uint256 _projectId,
        uint256 _pricePerTokenInWei
    ) external onlyArtist(_projectId) {
        projectConfig[_projectId].pricePerTokenInWei = _pricePerTokenInWei;
        projectConfig[_projectId].priceIsConfigured = true;
        emit PricePerTokenInWeiUpdated(_projectId, _pricePerTokenInWei);
    }

    /**
     * @notice Inactive function - requires Merkle proof to purchase.
     */
    function purchase(uint256) external payable returns (uint256) {
        revert("Must provide Merkle proof");
    }

    /**
     * @notice Inactive function - requires Merkle proof to purchase.
     */
    function purchaseTo(address, uint256) public payable returns (uint256) {
        revert("Must provide Merkle proof");
    }

    /**
     * @notice Purchases a token from project `_projectId`.
     * @param _projectId Project ID to mint a token on.
     * @param _proof Merkle proof.
     * @return tokenId Token ID of minted token
     */
    function purchase(
        uint256 _projectId,
        bytes32[] calldata _proof
    ) external payable returns (uint256 tokenId) {
        tokenId = purchaseTo_kem(msg.sender, _projectId, _proof, address(0));
        return tokenId;
    }

    /**
     * @notice gas-optimized version of purchase(uint256,bytes32[]).
     */
    function purchase_gD5(
        uint256 _projectId,
        bytes32[] calldata _proof
    ) external payable returns (uint256 tokenId) {
        tokenId = purchaseTo_kem(msg.sender, _projectId, _proof, address(0));
        return tokenId;
    }

    /**
     * @notice Purchases a token from project `_projectId` and sets
     * the token's owner to `_to`.
     * @param _to Address to be the new token's owner.
     * @param _projectId Project ID to mint a token on.
     * @param _proof Merkle proof.
     * @return tokenId Token ID of minted token
     */
    function purchaseTo(
        address _to,
        uint256 _projectId,
        bytes32[] calldata _proof
    ) external payable returns (uint256 tokenId) {
        return purchaseTo_kem(_to, _projectId, _proof, address(0));
    }

    /**
     * @notice Purchases a token from project `_projectId` and sets
     *         the token's owner to `_to`, as a delegate, (the `msg.sender`)
     *         on behalf of an explicitly defined vault.
     * @param _to Address to be the new token's owner.
     * @param _projectId Project ID to mint a token on.
     * @param _proof Merkle proof.
     * @param _vault Vault being purchased on behalf of.
     * @return tokenId Token ID of minted token
     */
    function purchaseTo(
        address _to,
        uint256 _projectId,
        bytes32[] calldata _proof,
        address _vault
    ) external payable returns (uint256 tokenId) {
        return purchaseTo_kem(_to, _projectId, _proof, _vault);
    }

    /**
     * @notice gas-optimized version of
     * purchaseTo(address,uint256,bytes32[],address).
     * @param _to Address to be the new token's owner.
     * @param _projectId Project ID to mint a token on.
     * @param _proof Merkle proof. Must be a valid proof of either `msg.sender`
     * if `_vault` is `address(0)`, or `_vault` if `_vault` is not `address(0)`.
     * @param _vault Vault being purchased on behalf of. Acceptable to be
     * address(0) if no vault.
     */
    function purchaseTo_kem(
        address _to,
        uint256 _projectId,
        bytes32[] calldata _proof,
        address _vault // acceptable to be `address(0)` if no vault
    ) public payable nonReentrant returns (uint256 tokenId) {
        // CHECKS
        ProjectConfig storage _projectConfig = projectConfig[_projectId];

        // Note that `maxHasBeenInvoked` is only checked here to reduce gas
        // consumption after a project has been fully minted.
        // `_projectConfig.maxHasBeenInvoked` is locally cached to reduce
        // gas consumption, but if not in sync with the core contract's value,
        // the core contract also enforces its own max invocation check during
        // minting.
        require(
            !_projectConfig.maxHasBeenInvoked,
            "Maximum number of invocations reached"
        );

        // load price of token into memory
        uint256 pricePerTokenInWei = _projectConfig.pricePerTokenInWei;

        require(
            msg.value >= pricePerTokenInWei,
            "Must send minimum value to mint!"
        );

        // require artist to have configured price of token on this minter
        require(_projectConfig.priceIsConfigured, "Price not configured");

        // no contract filter since Merkle tree controls allowed addresses

        // NOTE: delegate-vault handling **begins here**.

        // handle that the vault may be either the `msg.sender` in the case
        // that there is not a true vault, or may be `_vault` if one is
        // provided explicitly (and it is valid).
        address vault = msg.sender;
        if (_vault != address(0)) {
            // If a vault is provided, it must be valid, otherwise throw rather
            // than optimistically-minting with original `msg.sender`.
            // Note, we do not check `checkDelegateForAll` as well, as it is known
            // to be implicitly checked by calling `checkDelegateForContract`.
            bool isValidDelegee = delegationRegistryContract
                .checkDelegateForContract(
                    msg.sender, // delegate
                    _vault, // vault
                    genArt721CoreAddress // contract
                );
            require(isValidDelegee, "Invalid delegate-vault pairing");
            vault = _vault;
        }

        // require valid Merkle proof
        require(
            verifyAddress(_projectId, _proof, vault),
            "Invalid Merkle proof"
        );

        // limit mints per address by project
        uint256 _maxProjectInvocationsPerAddress = _projectConfig
            .useMaxInvocationsPerAddressOverride
            ? _projectConfig.maxInvocationsPerAddressOverride
            : DEFAULT_MAX_INVOCATIONS_PER_ADDRESS;

        // note that mint limits index off of the `vault` (when applicable)
        require(
            projectUserMintInvocations[_projectId][vault] <
                _maxProjectInvocationsPerAddress ||
                _maxProjectInvocationsPerAddress == 0,
            "Maximum number of invocations per address reached"
        );

        // EFFECTS
        // increment user's invocations for this project
        unchecked {
            // this will never overflow since user's invocations on a project
            // are limited by the project's max invocations
            projectUserMintInvocations[_projectId][vault]++;
        }

        // mint token
        tokenId = minterFilter.mint(_to, _projectId, vault);

        // NOTE: delegate-vault handling **ends here**.

        // okay if this underflows because if statement will always eval false.
        // this is only for gas optimization (core enforces maxInvocations).
        unchecked {
            if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
                _projectConfig.maxHasBeenInvoked = true;
            }
        }

        // INTERACTIONS
        splitFundsETH(_projectId, pricePerTokenInWei, genArt721CoreAddress);

        return tokenId;
    }

    /**
     * @notice projectId => maximum invocations per allowlisted address. If a
     * a value of 0 is returned, there is no limit on the number of mints per
     * allowlisted address.
     * Default behavior is limit 1 mint per address.
     * This value can be changed at any time by the artist.
     * @dev default value stated above must be updated if the value of
     * CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE is changed.
     */
    function projectMaxInvocationsPerAddress(
        uint256 _projectId
    ) public view returns (uint256) {
        ProjectConfig storage _projectConfig = projectConfig[_projectId];
        if (_projectConfig.useMaxInvocationsPerAddressOverride) {
            return uint256(_projectConfig.maxInvocationsPerAddressOverride);
        } else {
            return DEFAULT_MAX_INVOCATIONS_PER_ADDRESS;
        }
    }

    /**
     * @notice Returns remaining invocations for a given address.
     * If `projectLimitsMintInvocationsPerAddress` is false, individual
     * addresses are only limited by the project's maximum invocations, and a
     * dummy value of zero is returned for `mintInvocationsRemaining`.
     * If `projectLimitsMintInvocationsPerAddress` is true, the quantity of
     * remaining mint invocations for address `_address` is returned as
     * `mintInvocationsRemaining`.
     * Note that mint invocations per address can be changed at any time by the
     * artist of a project.
     * Also note that all mint invocations are limited by a project's maximum
     * invocations as defined on the core contract. This function may return
     * a value greater than the project's remaining invocations.
     */
    function projectRemainingInvocationsForAddress(
        uint256 _projectId,
        address _address
    )
        external
        view
        returns (
            bool projectLimitsMintInvocationsPerAddress,
            uint256 mintInvocationsRemaining
        )
    {
        uint256 maxInvocationsPerAddress = projectMaxInvocationsPerAddress(
            _projectId
        );
        if (maxInvocationsPerAddress == 0) {
            // project does not limit mint invocations per address, so leave
            // `projectLimitsMintInvocationsPerAddress` at solidity initial
            // value of false. Also leave `mintInvocationsRemaining` at
            // solidity initial value of zero, as indicated in this function's
            // documentation.
        } else {
            projectLimitsMintInvocationsPerAddress = true;
            uint256 userMintInvocations = projectUserMintInvocations[
                _projectId
            ][_address];
            // if user has not reached max invocations per address, return
            // remaining invocations
            if (maxInvocationsPerAddress > userMintInvocations) {
                unchecked {
                    // will never underflow due to the check above
                    mintInvocationsRemaining =
                        maxInvocationsPerAddress -
                        userMintInvocations;
                }
            }
            // else user has reached their maximum invocations, so leave
            // `mintInvocationsRemaining` at solidity initial value of zero
        }
    }

    /**
     * @notice Process proof for an address. Returns Merkle root. Included to
     * enable users to easily verify a proof's validity.
     * @param _proof Merkle proof for address.
     * @param _address Address to process.
     * @return merkleRoot Merkle root for `_address` and `_proof`
     */
    function processProofForAddress(
        bytes32[] calldata _proof,
        address _address
    ) external pure returns (bytes32) {
        return _proof.processProofCalldata(hashAddress(_address));
    }

    /**
     * @notice Gets if price of token is configured, price of minting a
     * token on project `_projectId`, and currency symbol and address to be
     * used as payment. Supersedes any core contract price information.
     * @param _projectId Project ID to get price information for.
     * @return isConfigured true only if token price has been configured on
     * this minter
     * @return tokenPriceInWei current price of token on this minter - invalid
     * if price has not yet been configured
     * @return currencySymbol currency symbol for purchases of project on this
     * minter. This minter always returns "ETH"
     * @return currencyAddress currency address for purchases of project on
     * this minter. This minter always returns null address, reserved for ether
     */
    function getPriceInfo(
        uint256 _projectId
    )
        external
        view
        returns (
            bool isConfigured,
            uint256 tokenPriceInWei,
            string memory currencySymbol,
            address currencyAddress
        )
    {
        ProjectConfig storage _projectConfig = projectConfig[_projectId];
        isConfigured = _projectConfig.priceIsConfigured;
        tokenPriceInWei = _projectConfig.pricePerTokenInWei;
        currencySymbol = "ETH";
        currencyAddress = address(0);
    }
}

File 2 of 19 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 3 of 19 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

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

File 4 of 19 : MerkleProof.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The proofs can be generated using the JavaScript library
 * https://github.com/miguelmota/merkletreejs[merkletreejs].
 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
 *
 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     *
     * _Available since v4.7._
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     *
     * _Available since v4.7._
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be proved to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * _Available since v4.7._
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * _Available since v4.7._
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and the sibling nodes in `proof`,
     * consuming from one or the other at each step according to the instructions given by
     * `proofFlags`.
     *
     * _Available since v4.7._
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}
     *
     * _Available since v4.7._
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

File 5 of 19 : 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 6 of 19 : IDelegationRegistry.sol
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.17;

/// @dev Source: https://github.com/0xfoobar/delegation-registry/blob/main/src/IDelegationRegistry.sol

/**
 * @title An immutable registry contract to be deployed as a standalone primitive
 * @dev See EIP-5639, new project launches can read previous cold wallet -> hot wallet delegations
 * from here and integrate those permissions into their flow
 */
interface IDelegationRegistry {
    /// @notice Delegation type
    enum DelegationType {
        NONE,
        ALL,
        CONTRACT,
        TOKEN
    }

    /// @notice Info about a single delegation, used for onchain enumeration
    struct DelegationInfo {
        DelegationType type_;
        address vault;
        address delegate;
        address contract_;
        uint256 tokenId;
    }

    /// @notice Info about a single contract-level delegation
    struct ContractDelegation {
        address contract_;
        address delegate;
    }

    /// @notice Info about a single token-level delegation
    struct TokenDelegation {
        address contract_;
        uint256 tokenId;
        address delegate;
    }

    /// @notice Emitted when a user delegates their entire wallet
    event DelegateForAll(address vault, address delegate, bool value);

    /// @notice Emitted when a user delegates a specific contract
    event DelegateForContract(
        address vault,
        address delegate,
        address contract_,
        bool value
    );

    /// @notice Emitted when a user delegates a specific token
    event DelegateForToken(
        address vault,
        address delegate,
        address contract_,
        uint256 tokenId,
        bool value
    );

    /// @notice Emitted when a user revokes all delegations
    event RevokeAllDelegates(address vault);

    /// @notice Emitted when a user revoes all delegations for a given delegate
    event RevokeDelegate(address vault, address delegate);

    /**
     * -----------  WRITE -----------
     */

    /**
     * @notice Allow the delegate to act on your behalf for all contracts
     * @param delegate The hotwallet to act on your behalf
     * @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
     */
    function delegateForAll(address delegate, bool value) external;

    /**
     * @notice Allow the delegate to act on your behalf for a specific contract
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
     */
    function delegateForContract(
        address delegate,
        address contract_,
        bool value
    ) external;

    /**
     * @notice Allow the delegate to act on your behalf for a specific token
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param tokenId The token id for the token you're delegating
     * @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
     */
    function delegateForToken(
        address delegate,
        address contract_,
        uint256 tokenId,
        bool value
    ) external;

    /**
     * @notice Revoke all delegates
     */
    function revokeAllDelegates() external;

    /**
     * @notice Revoke a specific delegate for all their permissions
     * @param delegate The hotwallet to revoke
     */
    function revokeDelegate(address delegate) external;

    /**
     * @notice Remove yourself as a delegate for a specific vault
     * @param vault The vault which delegated to the msg.sender, and should be removed
     */
    function revokeSelf(address vault) external;

    /**
     * -----------  READ -----------
     */

    /**
     * @notice Returns all active delegations a given delegate is able to claim on behalf of
     * @param delegate The delegate that you would like to retrieve delegations for
     * @return info Array of DelegationInfo structs
     */
    function getDelegationsByDelegate(
        address delegate
    ) external view returns (DelegationInfo[] memory);

    /**
     * @notice Returns an array of wallet-level delegates for a given vault
     * @param vault The cold wallet who issued the delegation
     * @return addresses Array of wallet-level delegates for a given vault
     */
    function getDelegatesForAll(
        address vault
    ) external view returns (address[] memory);

    /**
     * @notice Returns an array of contract-level delegates for a given vault and contract
     * @param vault The cold wallet who issued the delegation
     * @param contract_ The address for the contract you're delegating
     * @return addresses Array of contract-level delegates for a given vault and contract
     */
    function getDelegatesForContract(
        address vault,
        address contract_
    ) external view returns (address[] memory);

    /**
     * @notice Returns an array of contract-level delegates for a given vault's token
     * @param vault The cold wallet who issued the delegation
     * @param contract_ The address for the contract holding the token
     * @param tokenId The token id for the token you're delegating
     * @return addresses Array of contract-level delegates for a given vault's token
     */
    function getDelegatesForToken(
        address vault,
        address contract_,
        uint256 tokenId
    ) external view returns (address[] memory);

    /**
     * @notice Returns all contract-level delegations for a given vault
     * @param vault The cold wallet who issued the delegations
     * @return delegations Array of ContractDelegation structs
     */
    function getContractLevelDelegations(
        address vault
    ) external view returns (ContractDelegation[] memory delegations);

    /**
     * @notice Returns all token-level delegations for a given vault
     * @param vault The cold wallet who issued the delegations
     * @return delegations Array of TokenDelegation structs
     */
    function getTokenLevelDelegations(
        address vault
    ) external view returns (TokenDelegation[] memory delegations);

    /**
     * @notice Returns true if the address is delegated to act on the entire vault
     * @param delegate The hotwallet to act on your behalf
     * @param vault The cold wallet who issued the delegation
     */
    function checkDelegateForAll(
        address delegate,
        address vault
    ) external view returns (bool);

    /**
     * @notice Returns true if the address is delegated to act on your behalf for a token contract or an entire vault
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param vault The cold wallet who issued the delegation
     */
    function checkDelegateForContract(
        address delegate,
        address vault,
        address contract_
    ) external view returns (bool);

    /**
     * @notice Returns true if the address is delegated to act on your behalf for a specific token, the token's contract or an entire vault
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param tokenId The token id for the token you're delegating
     * @param vault The cold wallet who issued the delegation
     */
    function checkDelegateForToken(
        address delegate,
        address vault,
        address contract_,
        uint256 tokenId
    ) external view returns (bool);
}

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

import "./IFilteredMinterV1.sol";

pragma solidity ^0.8.0;

/**
 * @title This interface extends the IFilteredMinterV1 interface in order to
 * add support for including Merkle proofs when purchasing.
 * @author Art Blocks Inc.
 */
interface IFilteredMinterMerkleV0 is IFilteredMinterV1 {
    /**
     * @notice Notifies of the contract's default maximum mints allowed per
     * user for a given project, on this minter. This value can be overridden
     * by the artist of any project at any time.
     */
    event DefaultMaxInvocationsPerAddress(
        uint256 defaultMaxInvocationsPerAddress
    );

    // Triggers a purchase of a token from the desired project, to the
    // TX-sending address. Requires Merkle proof.
    function purchase(
        uint256 _projectId,
        bytes32[] memory _proof
    ) external payable returns (uint256 tokenId);

    // Triggers a purchase of a token from the desired project, to the specified
    // receiving address. Requires Merkle proof.
    function purchaseTo(
        address _to,
        uint256 _projectId,
        bytes32[] memory _proof
    ) external payable returns (uint256 tokenId);
}

File 8 of 19 : IFilteredMinterMerkleV1.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

import "./IFilteredMinterMerkleV0.sol";

pragma solidity ^0.8.0;

/**
 * @title This interface extends the IFilteredMinterMerkleV0 interface in order
 * to add support for configuring and indexing the delegation registry address.
 * @author Art Blocks Inc.
 */
interface IFilteredMinterMerkleV1 is IFilteredMinterMerkleV0 {
    /**
     * @notice Notifies of the contract's configured delegation registry
     * address.
     */
    event DelegationRegistryUpdated(address delegationRegistryAddress);
}

File 9 of 19 : IFilteredMinterMerkleV2.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

import "./IFilteredMinterMerkleV1.sol";
import "./IFilteredMinterV2.sol";

pragma solidity ^0.8.0;

/**
 * @title This interface extends the IFilteredMinterMerkleV0 interface in order to
 * add support for manually setting project max invocations.
 * @author Art Blocks Inc.
 */
interface IFilteredMinterMerkleV2 is
    IFilteredMinterMerkleV1,
    IFilteredMinterV2
{

}

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

pragma solidity ^0.8.0;

interface IFilteredMinterV0 {
    /**
     * @notice Price per token in wei updated for project `_projectId` to
     * `_pricePerTokenInWei`.
     */
    event PricePerTokenInWeiUpdated(
        uint256 indexed _projectId,
        uint256 indexed _pricePerTokenInWei
    );

    /**
     * @notice Currency updated for project `_projectId` to symbol
     * `_currencySymbol` and address `_currencyAddress`.
     */
    event ProjectCurrencyInfoUpdated(
        uint256 indexed _projectId,
        address indexed _currencyAddress,
        string _currencySymbol
    );

    /// togglePurchaseToDisabled updated
    event PurchaseToDisabledUpdated(
        uint256 indexed _projectId,
        bool _purchaseToDisabled
    );

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

    function genArt721CoreAddress() external returns (address);

    function minterFilterAddress() external returns (address);

    // Triggers a purchase of a token from the desired project, to the
    // TX-sending address.
    function purchase(
        uint256 _projectId
    ) external payable returns (uint256 tokenId);

    // Triggers a purchase of a token from the desired project, to the specified
    // receiving address.
    function purchaseTo(
        address _to,
        uint256 _projectId
    ) external payable returns (uint256 tokenId);

    // Toggles the ability for `purchaseTo` to be called directly with a
    // specified receiving address that differs from the TX-sending address.
    function togglePurchaseToDisabled(uint256 _projectId) external;

    // Called to make the minter contract aware of the max invocations for a
    // given project.
    function setProjectMaxInvocations(uint256 _projectId) external;

    // 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
    )
        external
        view
        returns (
            bool isConfigured,
            uint256 tokenPriceInWei,
            string memory currencySymbol,
            address currencyAddress
        );
}

File 11 of 19 : IFilteredMinterV1.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

import "./IFilteredMinterV0.sol";

pragma solidity ^0.8.0;

/**
 * @title This interface extends the IFilteredMinterV0 interface in order to
 * add support for generic project minter configuration updates.
 * @dev keys represent strings of finite length encoded in bytes32 to minimize
 * gas.
 * @author Art Blocks Inc.
 */
interface IFilteredMinterV1 is IFilteredMinterV0 {
    /// ANY
    /**
     * @notice Generic project minter configuration event. Removes key `_key`
     * for project `_projectId`.
     */
    event ConfigKeyRemoved(uint256 indexed _projectId, bytes32 _key);

    /// BOOL
    /**
     * @notice Generic project minter configuration event. Sets value of key
     * `_key` to `_value` for project `_projectId`.
     */
    event ConfigValueSet(uint256 indexed _projectId, bytes32 _key, bool _value);

    /// UINT256
    /**
     * @notice Generic project minter configuration event. Sets value of key
     * `_key` to `_value` for project `_projectId`.
     */
    event ConfigValueSet(
        uint256 indexed _projectId,
        bytes32 _key,
        uint256 _value
    );

    /**
     * @notice Generic project minter configuration event. Adds value `_value`
     * to the set of uint256 at key `_key` for project `_projectId`.
     */
    event ConfigValueAddedToSet(
        uint256 indexed _projectId,
        bytes32 _key,
        uint256 _value
    );

    /**
     * @notice Generic project minter configuration event. Removes value
     * `_value` to the set of uint256 at key `_key` for project `_projectId`.
     */
    event ConfigValueRemovedFromSet(
        uint256 indexed _projectId,
        bytes32 _key,
        uint256 _value
    );

    /// ADDRESS
    /**
     * @notice Generic project minter configuration event. Sets value of key
     * `_key` to `_value` for project `_projectId`.
     */
    event ConfigValueSet(
        uint256 indexed _projectId,
        bytes32 _key,
        address _value
    );

    /**
     * @notice Generic project minter configuration event. Adds value `_value`
     * to the set of addresses at key `_key` for project `_projectId`.
     */
    event ConfigValueAddedToSet(
        uint256 indexed _projectId,
        bytes32 _key,
        address _value
    );

    /**
     * @notice Generic project minter configuration event. Removes value
     * `_value` to the set of addresses at key `_key` for project `_projectId`.
     */
    event ConfigValueRemovedFromSet(
        uint256 indexed _projectId,
        bytes32 _key,
        address _value
    );

    /// BYTES32
    /**
     * @notice Generic project minter configuration event. Sets value of key
     * `_key` to `_value` for project `_projectId`.
     */
    event ConfigValueSet(
        uint256 indexed _projectId,
        bytes32 _key,
        bytes32 _value
    );

    /**
     * @notice Generic project minter configuration event. Adds value `_value`
     * to the set of bytes32 at key `_key` for project `_projectId`.
     */
    event ConfigValueAddedToSet(
        uint256 indexed _projectId,
        bytes32 _key,
        bytes32 _value
    );

    /**
     * @notice Generic project minter configuration event. Removes value
     * `_value` to the set of bytes32 at key `_key` for project `_projectId`.
     */
    event ConfigValueRemovedFromSet(
        uint256 indexed _projectId,
        bytes32 _key,
        bytes32 _value
    );

    /**
     * @dev Strings not supported. Recommend conversion of (short) strings to
     * bytes32 to remain gas-efficient.
     */
}

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

import "./IFilteredMinterV1.sol";

pragma solidity ^0.8.0;

/**
 * @title This interface extends the IFilteredMinterV1 interface in order to
 * add support for manually setting project max invocations.
 * @author Art Blocks Inc.
 */
interface IFilteredMinterV2 is IFilteredMinterV1 {
    /**
     * @notice Local max invocations for project `_projectId`, tied to core contract `_coreContractAddress`,
     * updated to `_maxInvocations`.
     */
    event ProjectMaxInvocationsLimitUpdated(
        uint256 indexed _projectId,
        uint256 _maxInvocations
    );

    // 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,
        uint256 _maxInvocations
    ) external;
}

File 13 of 19 : 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 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);

    // @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 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 14 of 19 : IGenArt721CoreContractV3_Engine.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

import "./IAdminACLV0.sol";
import "./IGenArt721CoreContractV3_Base.sol";

interface IGenArt721CoreContractV3_Engine is IGenArt721CoreContractV3_Base {
    // @dev new function in V3
    function getPrimaryRevenueSplits(
        uint256 _projectId,
        uint256 _price
    )
        external
        view
        returns (
            uint256 renderProviderRevenue_,
            address payable renderProviderAddress_,
            uint256 platformProviderRevenue_,
            address payable platformProviderAddress_,
            uint256 artistRevenue_,
            address payable artistAddress_,
            uint256 additionalPayeePrimaryRevenue_,
            address payable additionalPayeePrimaryAddress_
        );

    // @dev The render provider primary sales payment address
    function renderProviderPrimarySalesAddress()
        external
        view
        returns (address payable);

    // @dev The platform provider primary sales payment address
    function platformProviderPrimarySalesAddress()
        external
        view
        returns (address payable);

    // @dev Percentage of primary sales allocated to the render provider
    function renderProviderPrimarySalesPercentage()
        external
        view
        returns (uint256);

    // @dev Percentage of primary sales allocated to the platform provider
    function platformProviderPrimarySalesPercentage()
        external
        view
        returns (uint256);

    // @dev The render provider secondary sales royalties payment address
    function renderProviderSecondarySalesAddress()
        external
        view
        returns (address payable);

    // @dev The platform provider secondary sales royalties payment address
    function platformProviderSecondarySalesAddress()
        external
        view
        returns (address payable);

    // @dev Basis points of secondary sales allocated to the render provider
    function renderProviderSecondarySalesBPS() external view returns (uint256);

    // @dev Basis points of secondary sales allocated to the platform provider
    function platformProviderSecondarySalesBPS()
        external
        view
        returns (uint256);

    // function to read the hash for a given tokenId
    function tokenIdToHash(uint256 _tokenId) external view returns (bytes32);

    // function to read the hash-seed for a given tokenId
    function tokenIdToHashSeed(
        uint256 _tokenId
    ) external view returns (bytes12);
}

File 15 of 19 : IGenArt721CoreContractV3.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

import "./IAdminACLV0.sol";
import "./IGenArt721CoreContractV3_Base.sol";

/**
 * @title This interface extends IGenArt721CoreContractV3_Base with functions
 * that are part of the Art Blocks Flagship core contract.
 * @author Art Blocks Inc.
 */
// This interface extends IGenArt721CoreContractV3_Base with functions that are
// in part of the Art Blocks Flagship core contract.
interface IGenArt721CoreContractV3 is IGenArt721CoreContractV3_Base {
    // @dev new function in V3
    function getPrimaryRevenueSplits(
        uint256 _projectId,
        uint256 _price
    )
        external
        view
        returns (
            uint256 artblocksRevenue_,
            address payable artblocksAddress_,
            uint256 artistRevenue_,
            address payable artistAddress_,
            uint256 additionalPayeePrimaryRevenue_,
            address payable additionalPayeePrimaryAddress_
        );

    // @dev Art Blocks primary sales payment address
    function artblocksPrimarySalesAddress()
        external
        view
        returns (address payable);

    /**
     * @notice Backwards-compatible (pre-V3) function returning Art Blocks
     * primary sales payment address (now called artblocksPrimarySalesAddress).
     */
    function artblocksAddress() external view returns (address payable);

    // @dev Percentage of primary sales allocated to Art Blocks
    function artblocksPrimarySalesPercentage() external view returns (uint256);

    /**
     * @notice Backwards-compatible (pre-V3) function returning Art Blocks
     * primary sales percentage (now called artblocksPrimarySalesPercentage).
     */
    function artblocksPercentage() external view returns (uint256);

    // @dev Art Blocks secondary sales royalties payment address
    function artblocksSecondarySalesAddress()
        external
        view
        returns (address payable);

    // @dev Basis points of secondary sales allocated to Art Blocks
    function artblocksSecondarySalesBPS() external view returns (uint256);

    /**
     * @notice Backwards-compatible (pre-V3) function  that gets artist +
     * artist's additional payee royalty data for token ID `_tokenId`.
     * WARNING: Does not include Art Blocks portion of royalties.
     */
    function getRoyaltyData(
        uint256 _tokenId
    )
        external
        view
        returns (
            address artistAddress,
            address additionalPayee,
            uint256 additionalPayeePercentage,
            uint256 royaltyFeeByID
        );
}

File 16 of 19 : 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 17 of 19 : IMinterBaseV0.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

import "./IFilteredMinterV2.sol";

pragma solidity ^0.8.0;

/**
 * @title This interface defines any events or functions required for a minter
 * to conform to the MinterBase contract.
 * @dev The MinterBase contract was not implemented from the beginning of the
 * MinterSuite contract suite, therefore early versions of some minters may not
 * conform to this interface.
 * @author Art Blocks Inc.
 */
interface IMinterBaseV0 {
    // Function that returns if a minter is configured to integrate with a V3 flagship or V3 engine contract.
    // Returns true only if the minter is configured to integrate with an engine contract.
    function isEngine() external returns (bool isEngine);
}

File 18 of 19 : IMinterFilterV0.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

interface IMinterFilterV0 {
    /**
     * @notice Approved minter `_minterAddress`.
     */
    event MinterApproved(address indexed _minterAddress, string _minterType);

    /**
     * @notice Revoked approval for minter `_minterAddress`
     */
    event MinterRevoked(address indexed _minterAddress);

    /**
     * @notice Minter `_minterAddress` of type `_minterType`
     * registered for project `_projectId`.
     */
    event ProjectMinterRegistered(
        uint256 indexed _projectId,
        address indexed _minterAddress,
        string _minterType
    );

    /**
     * @notice Any active minter removed for project `_projectId`.
     */
    event ProjectMinterRemoved(uint256 indexed _projectId);

    function genArt721CoreAddress() external returns (address);

    function setMinterForProject(uint256, address) external;

    function removeMinterForProject(uint256) external;

    function mint(
        address _to,
        uint256 _projectId,
        address sender
    ) external returns (uint256);

    function getMinterForProject(uint256) external view returns (address);

    function projectHasMinter(uint256) external view returns (bool);
}

File 19 of 19 : MinterBase_v0_1_1.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

import "../../interfaces/0.8.x/IMinterBaseV0.sol";
import "../../interfaces/0.8.x/IGenArt721CoreContractV3_Base.sol";
import "../../interfaces/0.8.x/IGenArt721CoreContractV3.sol";
import "../../interfaces/0.8.x/IGenArt721CoreContractV3_Engine.sol";

import "@openzeppelin-4.7/contracts/token/ERC20/IERC20.sol";

pragma solidity ^0.8.0;

/**
 * @title Art Blocks Minter Base Class
 * @notice A base class for Art Blocks minter contracts that provides common
 * functionality used across minter contracts.
 * This contract is not intended to be deployed directly, but rather to be
 * inherited by other minter contracts.
 * From a design perspective, this contract is intended to remain simple and
 * easy to understand. It is not intended to cause a complex inheritance tree,
 * and instead should keep minter contracts as readable as possible for
 * collectors and developers.
 * @dev Semantic versioning is used in the solidity file name, and is therefore
 * controlled by contracts importing the appropriate filename version.
 * @author Art Blocks Inc.
 */
abstract contract MinterBase is IMinterBaseV0 {
    /// state variable that tracks whether this contract's associated core
    /// contract is an Engine contract, where Engine contracts have an
    /// additional revenue split for the platform provider
    bool public immutable isEngine;

    // @dev we do not track an initialization state, as the only state variable
    // is immutable, which the compiler enforces to be assigned during
    // construction.

    /**
     * @notice Initializes contract to ensure state variable `isEngine` is set
     * appropriately based on the minter's associated core contract address.
     * @param genArt721Address Art Blocks core contract address for
     * which this contract will be a minter.
     */
    constructor(address genArt721Address) {
        // set state variable isEngine
        isEngine = _getV3CoreIsEngine(genArt721Address);
    }

    /**
     * @notice splits ETH funds between sender (if refund), providers,
     * artist, and artist's additional payee for a token purchased on
     * project `_projectId`.
     * WARNING: This function uses msg.value and msg.sender to determine
     * refund amounts, and therefore may not be applicable to all use cases
     * (e.g. do not use with Dutch Auctions with on-chain settlement).
     * @dev possible DoS during splits is acknowledged, and mitigated by
     * business practices, including end-to-end testing on mainnet, and
     * admin-accepted artist payment addresses.
     * @param projectId Project ID for which funds shall be split.
     * @param pricePerTokenInWei Current price of token, in Wei.
     */
    function splitFundsETH(
        uint256 projectId,
        uint256 pricePerTokenInWei,
        address genArt721CoreAddress
    ) internal {
        if (msg.value > 0) {
            bool success_;
            // send refund to sender
            uint256 refund = msg.value - pricePerTokenInWei;
            if (refund > 0) {
                (success_, ) = msg.sender.call{value: refund}("");
                require(success_, "Refund failed");
            }
            // split revenues
            splitRevenuesETH(
                projectId,
                pricePerTokenInWei,
                genArt721CoreAddress
            );
        }
    }

    /**
     * @notice splits ETH revenues between providers, artist, and artist's
     * additional payee for revenue generated by project `_projectId`.
     * @dev possible DoS during splits is acknowledged, and mitigated by
     * business practices, including end-to-end testing on mainnet, and
     * admin-accepted artist payment addresses.
     * @param projectId Project ID for which funds shall be split.
     * @param valueInWei Value to be split, in Wei.
     */
    function splitRevenuesETH(
        uint256 projectId,
        uint256 valueInWei,
        address genArtCoreContract
    ) internal {
        if (valueInWei <= 0) {
            return; // return early
        }
        bool success;
        // split funds between platforms, artist, and artist's
        // additional payee
        uint256 renderProviderRevenue_;
        address payable renderProviderAddress_;
        uint256 artistRevenue_;
        address payable artistAddress_;
        uint256 additionalPayeePrimaryRevenue_;
        address payable additionalPayeePrimaryAddress_;
        if (isEngine) {
            // get engine splits
            uint256 platformProviderRevenue_;
            address payable platformProviderAddress_;
            (
                renderProviderRevenue_,
                renderProviderAddress_,
                platformProviderRevenue_,
                platformProviderAddress_,
                artistRevenue_,
                artistAddress_,
                additionalPayeePrimaryRevenue_,
                additionalPayeePrimaryAddress_
            ) = IGenArt721CoreContractV3_Engine(genArtCoreContract)
                .getPrimaryRevenueSplits(projectId, valueInWei);
            // Platform Provider payment (only possible if engine)
            if (platformProviderRevenue_ > 0) {
                (success, ) = platformProviderAddress_.call{
                    value: platformProviderRevenue_
                }("");
                require(success, "Platform Provider payment failed");
            }
        } else {
            // get flagship splits
            (
                renderProviderRevenue_, // artblocks revenue
                renderProviderAddress_, // artblocks address
                artistRevenue_,
                artistAddress_,
                additionalPayeePrimaryRevenue_,
                additionalPayeePrimaryAddress_
            ) = IGenArt721CoreContractV3(genArtCoreContract)
                .getPrimaryRevenueSplits(projectId, valueInWei);
        }
        // Render Provider / Art Blocks payment
        if (renderProviderRevenue_ > 0) {
            (success, ) = renderProviderAddress_.call{
                value: renderProviderRevenue_
            }("");
            require(success, "Render Provider payment failed");
        }
        // artist payment
        if (artistRevenue_ > 0) {
            (success, ) = artistAddress_.call{value: artistRevenue_}("");
            require(success, "Artist payment failed");
        }
        // additional payee payment
        if (additionalPayeePrimaryRevenue_ > 0) {
            (success, ) = additionalPayeePrimaryAddress_.call{
                value: additionalPayeePrimaryRevenue_
            }("");
            require(success, "Additional Payee payment failed");
        }
    }

    /**
     * @notice splits ERC-20 funds between providers, artist, and artist's
     * additional payee, for a token purchased on project `_projectId`.
     * @dev possible DoS during splits is acknowledged, and mitigated by
     * business practices, including end-to-end testing on mainnet, and
     * admin-accepted artist payment addresses.
     */
    function splitFundsERC20(
        uint256 projectId,
        uint256 pricePerTokenInWei,
        address currencyAddress,
        address genArtCoreContract
    ) internal {
        IERC20 _projectCurrency = IERC20(currencyAddress);
        // split remaining funds between foundation, artist, and artist's
        // additional payee
        uint256 renderProviderRevenue_;
        address payable renderProviderAddress_;
        uint256 artistRevenue_;
        address payable artistAddress_;
        uint256 additionalPayeePrimaryRevenue_;
        address payable additionalPayeePrimaryAddress_;
        if (isEngine) {
            // get engine splits
            uint256 platformProviderRevenue_;
            address payable platformProviderAddress_;
            (
                renderProviderRevenue_,
                renderProviderAddress_,
                platformProviderRevenue_,
                platformProviderAddress_,
                artistRevenue_,
                artistAddress_,
                additionalPayeePrimaryRevenue_,
                additionalPayeePrimaryAddress_
            ) = IGenArt721CoreContractV3_Engine(genArtCoreContract)
                .getPrimaryRevenueSplits(projectId, pricePerTokenInWei);
            // Platform Provider payment (only possible if engine)
            if (platformProviderRevenue_ > 0) {
                _projectCurrency.transferFrom(
                    msg.sender,
                    platformProviderAddress_,
                    platformProviderRevenue_
                );
            }
        } else {
            // get flagship splits
            (
                renderProviderRevenue_, // artblocks revenue
                renderProviderAddress_, // artblocks address
                artistRevenue_,
                artistAddress_,
                additionalPayeePrimaryRevenue_,
                additionalPayeePrimaryAddress_
            ) = IGenArt721CoreContractV3(genArtCoreContract)
                .getPrimaryRevenueSplits(projectId, pricePerTokenInWei);
        }
        // Art Blocks payment
        if (renderProviderRevenue_ > 0) {
            _projectCurrency.transferFrom(
                msg.sender,
                renderProviderAddress_,
                renderProviderRevenue_
            );
        }
        // artist payment
        if (artistRevenue_ > 0) {
            _projectCurrency.transferFrom(
                msg.sender,
                artistAddress_,
                artistRevenue_
            );
        }
        // additional payee payment
        if (additionalPayeePrimaryRevenue_ > 0) {
            _projectCurrency.transferFrom(
                msg.sender,
                additionalPayeePrimaryAddress_,
                additionalPayeePrimaryRevenue_
            );
        }
    }

    /**
     * @notice Returns whether a V3 core contract is an Art Blocks Engine
     * contract or not. Return value of false indicates that the core is a
     * flagship contract.
     * @dev this function reverts if a core contract does not return the
     * expected number of return values from getPrimaryRevenueSplits() for
     * either a flagship or engine core contract.
     * @dev this function uses the length of the return data (in bytes) to
     * determine whether the core is an engine or not.
     * @param genArt721CoreV3 The address of the deployed core contract.
     */
    function _getV3CoreIsEngine(
        address genArt721CoreV3
    ) private returns (bool) {
        // call getPrimaryRevenueSplits() on core contract
        bytes memory payload = abi.encodeWithSignature(
            "getPrimaryRevenueSplits(uint256,uint256)",
            0,
            0
        );
        (bool success, bytes memory returnData) = genArt721CoreV3.call(payload);
        require(success, "getPrimaryRevenueSplits() call failed");
        // determine whether core is engine or not, based on return data length
        uint256 returnDataLength = returnData.length;
        if (returnDataLength == 6 * 32) {
            // 6 32-byte words returned if flagship (not engine)
            // @dev 6 32-byte words are expected because the non-engine core
            // contracts return a payout address and uint256 payment value for
            // the artist, and artist's additional payee, and Art Blocks.
            // also note that per Solidity ABI encoding, the address return
            // values are padded to 32 bytes.
            return false;
        } else if (returnDataLength == 8 * 32) {
            // 8 32-byte words returned if engine
            // @dev 8 32-byte words are expected because the engine core
            // contracts return a payout address and uint256 payment value for
            // the artist, artist's additional payee, render provider
            // typically Art Blocks, and platform provider (partner).
            // also note that per Solidity ABI encoding, the address return
            // values are padded to 32 bytes.
            return true;
        } else {
            // unexpected return value length
            revert("Unexpected revenue split bytes");
        }
    }
}

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":"_genArt721Address","type":"address"},{"internalType":"address","name":"_minterFilter","type":"address"},{"internalType":"address","name":"_delegationRegistryAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"}],"name":"ConfigKeyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"ConfigValueAddedToSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"address","name":"_value","type":"address"}],"name":"ConfigValueAddedToSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"_value","type":"bytes32"}],"name":"ConfigValueAddedToSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"ConfigValueRemovedFromSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"address","name":"_value","type":"address"}],"name":"ConfigValueRemovedFromSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"_value","type":"bytes32"}],"name":"ConfigValueRemovedFromSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"_value","type":"bool"}],"name":"ConfigValueSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"ConfigValueSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"address","name":"_value","type":"address"}],"name":"ConfigValueSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"_value","type":"bytes32"}],"name":"ConfigValueSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"defaultMaxInvocationsPerAddress","type":"uint256"}],"name":"DefaultMaxInvocationsPerAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"delegationRegistryAddress","type":"address"}],"name":"DelegationRegistryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_pricePerTokenInWei","type":"uint256"}],"name":"PricePerTokenInWeiUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"_currencyAddress","type":"address"},{"indexed":false,"internalType":"string","name":"_currencySymbol","type":"string"}],"name":"ProjectCurrencyInfoUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_maxInvocations","type":"uint256"}],"name":"ProjectMaxInvocationsLimitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"_purchaseToDisabled","type":"bool"}],"name":"PurchaseToDisabledUpdated","type":"event"},{"inputs":[],"name":"DEFAULT_MAX_INVOCATIONS_PER_ADDRESS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delegationRegistryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"genArt721CoreAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"getPriceInfo","outputs":[{"internalType":"bool","name":"isConfigured","type":"bool"},{"internalType":"uint256","name":"tokenPriceInWei","type":"uint256"},{"internalType":"string","name":"currencySymbol","type":"string"},{"internalType":"address","name":"currencyAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"hashAddress","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"isEngine","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_maxInvocations","type":"uint256"}],"name":"manuallyLimitProjectMaxInvocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minterFilterAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterType","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"},{"internalType":"address","name":"_address","type":"address"}],"name":"processProofForAddress","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"projectConfig","outputs":[{"internalType":"bool","name":"maxHasBeenInvoked","type":"bool"},{"internalType":"bool","name":"priceIsConfigured","type":"bool"},{"internalType":"bool","name":"useMaxInvocationsPerAddressOverride","type":"bool"},{"internalType":"uint24","name":"maxInvocationsPerAddressOverride","type":"uint24"},{"internalType":"uint24","name":"maxInvocations","type":"uint24"},{"internalType":"uint256","name":"pricePerTokenInWei","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectMaxHasBeenInvoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectMaxInvocations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectMaxInvocationsPerAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"projectMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address","name":"_address","type":"address"}],"name":"projectRemainingInvocationsForAddress","outputs":[{"internalType":"bool","name":"projectLimitsMintInvocationsPerAddress","type":"bool"},{"internalType":"uint256","name":"mintInvocationsRemaining","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"projectUserMintInvocations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"}],"name":"purchase","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"purchase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"}],"name":"purchaseTo","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"purchaseTo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"},{"internalType":"address","name":"_vault","type":"address"}],"name":"purchaseTo","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"},{"internalType":"address","name":"_vault","type":"address"}],"name":"purchaseTo_kem","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"}],"name":"purchase_gD5","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint24","name":"_maxInvocationsPerAddress","type":"uint24"}],"name":"setProjectInvocationsPerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"setProjectMaxInvocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"togglePurchaseToDisabled","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes32","name":"_root","type":"bytes32"}],"name":"updateMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_pricePerTokenInWei","type":"uint256"}],"name":"updatePricePerTokenInWei","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"},{"internalType":"address","name":"_address","type":"address"}],"name":"verifyAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

6101606040523480156200001257600080fd5b506040516200268d3803806200268d833981016040819052620000359162000356565b6001600055826200004681620001b8565b1515608052506001600160a01b0383811660e081905261010052811660a08190526040519081527f836360d1b094a7de3c3eab3d1185f3a5939467c23d4a12709dbdbf8c8d7e2f3b9060200160405180910390a16001600160a01b0380821660c052828116610120819052610140819052604080516392a10f8360e01b81529051928616926392a10f839160048082019260209290919082900301816000875af1158015620000f9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200011f9190620003a0565b6001600160a01b0316146200017b5760405162461bcd60e51b815260206004820152601860248201527f496c6c6567616c20636f6e74726163742070616972696e67000000000000000060448201526064015b60405180910390fd5b604051600181527fb004e34bc1718c504fde68324f2eba80b0329056f240eaaa6803c286c4b9e7ed9060200160405180910390a1505050620003f6565b6040516000602482018190526044820181905290819060640160408051601f198184030181529181526020820180516001600160e01b0316638639415b60e01b1790525190915060009081906001600160a01b038616906200021c908590620003c5565b6000604051808303816000865af19150503d80600081146200025b576040519150601f19603f3d011682016040523d82523d6000602084013e62000260565b606091505b509150915081620002c25760405162461bcd60e51b815260206004820152602560248201527f6765745072696d617279526576656e756553706c69747328292063616c6c2066604482015264185a5b195960da1b606482015260840162000172565b805160c0819003620002da5750600095945050505050565b8061010003620002f05750600195945050505050565b60405162461bcd60e51b815260206004820152601e60248201527f556e657870656374656420726576656e75652073706c69742062797465730000604482015260640162000172565b80516001600160a01b03811681146200035157600080fd5b919050565b6000806000606084860312156200036c57600080fd5b620003778462000339565b9250620003876020850162000339565b9150620003976040850162000339565b90509250925092565b600060208284031215620003b357600080fd5b620003be8262000339565b9392505050565b6000825160005b81811015620003e85760208186018101518583015201620003cc565b506000920191825250919050565b60805160a05160c05160e0516101005161012051610140516121f6620004976000396000610a81015260006105db015260008181610bc401528181610c8a01528181610f1101528181611060015281816111f3015281816113a0015281816114ff01526115c50152600081816103db015281816108350152610b2401526000610860015260006104740152600081816105a701526117d701526121f66000f3fe6080604052600436106101695760003560e01c806392a10f83116100c757806392a10f83146103c95780639775f86f14610415578063aea7d3b91461042a578063bf5bf5f814610462578063c15127c014610496578063c71b1b71146104a9578063cc3352bf14610548578063d684ec6714610568578063da7e7c5014610194578063db6921b214610595578063dd85582f146105c9578063e9d1e8ac146105fd578063efef39a114610644578063f1e3311514610652578063f7bd4b881461067257600080fd5b806157b61461016e5780615aa11461019457806315bed159146101a75780631607c995146101c75780631ec2e523146101e9578063202c5805146102095780633aa5fe591461021c57806340d1397e1461023c578063462add461461025c57806356690aaf1461029c5780636b453e70146102d55780636cb9b7ff1461030c5780636ede4ade1461032c578063774159c61461034c578063891407c0146103b6575b600080fd5b61018161017c366004611c45565b610692565b6040519081526020015b60405180910390f35b6101816101a2366004611cb8565b610b5a565b3480156101b357600080fd5b506101816101c2366004611d03565b610b72565b3480156101d357600080fd5b506101e76101e2366004611d1c565b610bac565b005b3480156101f557600080fd5b50610181610204366004611d3e565b610e8f565b610181610217366004611d94565b610ea6565b34801561022857600080fd5b50610181610237366004611def565b610ebf565b34801561024857600080fd5b506101e7610257366004611d03565b610ef9565b34801561026857600080fd5b5061028c610277366004611d03565b60009081526001602052604090205460ff1690565b604051901515815260200161018b565b3480156102a857600080fd5b506101816102b7366004611d03565b600090815260016020526040902054600160301b900462ffffff1690565b3480156102e157600080fd5b506102f56102f0366004611e13565b610ff3565b60408051921515835260208301919091520161018b565b34801561031857600080fd5b506101e7610327366004611d1c565b611048565b34801561033857600080fd5b5061028c610347366004611e43565b611156565b34801561035857600080fd5b506103a6610367366004611d03565b600090815260016020818152604080842080549301548151808301909252600382526208aa8960eb1b9282019290925261010090920460ff1693909290565b60405161018b9493929190611ee7565b6101816103c4366004611f21565b61117b565b3480156103d557600080fd5b506103fd7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161018b565b34801561042157600080fd5b50610181600181565b34801561043657600080fd5b50610181610445366004611e13565b600360209081526000928352604080842090915290825290205481565b34801561046e57600080fd5b506103fd7f000000000000000000000000000000000000000000000000000000000000000081565b6101816104a4366004611c45565b6111c2565b3480156104b557600080fd5b5061050c6104c4366004611d03565b6001602081905260009182526040909120805491015460ff8083169261010081048216926201000082049092169162ffffff63010000008304811692600160301b9004169086565b60408051961515875294151560208701529215159385019390935262ffffff9081166060850152909116608083015260a082015260c00161018b565b34801561055457600080fd5b506101e7610563366004611f4d565b6111db565b34801561057457600080fd5b50610181610583366004611d03565b60026020526000908152604090205481565b3480156105a157600080fd5b5061028c7f000000000000000000000000000000000000000000000000000000000000000081565b3480156105d557600080fd5b506103fd7f000000000000000000000000000000000000000000000000000000000000000081565b34801561060957600080fd5b506106376040518060400160405280600e81526020016d4d696e7465724d65726b6c65563560901b81525081565b60405161018b9190611f7a565b6101816103c4366004611d03565b34801561065e57600080fd5b506101e761066d366004611d1c565b611388565b34801561067e57600080fd5b506101e761068d366004611d03565b6114e7565b60006002600054036106eb5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60026000908155858152600160205260409020805460ff161561075e5760405162461bcd60e51b815260206004820152602560248201527f4d6178696d756d206e756d626572206f6620696e766f636174696f6e732072656044820152641858da195960da1b60648201526084016106e2565b6001810154348111156107b35760405162461bcd60e51b815260206004820181905260248201527f4d7573742073656e64206d696e696d756d2076616c756520746f206d696e742160448201526064016106e2565b8154610100900460ff166108005760405162461bcd60e51b8152602060048201526014602482015273141c9a58d9481b9bdd0818dbdb999a59dd5c995960621b60448201526064016106e2565b336001600160a01b038516156109215760405163090c9a2d60e41b81523360048201526001600160a01b0386811660248301527f0000000000000000000000000000000000000000000000000000000000000000811660448301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906390c9a2d090606401602060405180830381865afa1580156108a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108cd9190611fa2565b90508061091c5760405162461bcd60e51b815260206004820152601e60248201527f496e76616c69642064656c65676174652d7661756c742070616972696e67000060448201526064016106e2565b859150505b61092d88888884611156565b6109705760405162461bcd60e51b815260206004820152601460248201527324b73b30b634b21026b2b935b63290383937b7b360611b60448201526064016106e2565b825460009062010000900460ff16610989576001610998565b83546301000000900462ffffff165b60008a81526003602090815260408083206001600160a01b03871684529091529020549091508111806109c9575080155b610a2f5760405162461bcd60e51b815260206004820152603160248201527f4d6178696d756d206e756d626572206f6620696e766f636174696f6e732070656044820152701c881859191c995cdcc81c995858da1959607a1b60648201526084016106e2565b60008981526003602090815260408083206001600160a01b0386811680865291909352928190208054600101905551630d4d151360e01b81528c82166004820152602481018c905260448101929092527f00000000000000000000000000000000000000000000000000000000000000001690630d4d1513906064016020604051808303816000875af1158015610aca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aee9190611fbd565b845490955060001962ffffff600160301b90920482160116620f4240860603610b1d57835460ff191660011784555b610b4889847f00000000000000000000000000000000000000000000000000000000000000006116a3565b50505050600160005595945050505050565b6000610b6a338585856000610692565b949350505050565b6000818152600160205260408120805462010000900460ff1615610ba357546301000000900462ffffff1692915050565b50600192915050565b60405163a47d29cb60e01b81526004810183905282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a47d29cb90602401602060405180830381865afa158015610c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c379190611fd6565b6001600160a01b0316336001600160a01b031614610c675760405162461bcd60e51b81526004016106e290611ff3565b604051630ea5613f60e01b81526004810184905260009081906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690630ea5613f9060240160c060405180830381865afa158015610cd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf59190612018565b5092955092935050505081841115610d955760405162461bcd60e51b815260206004820152605760248201527f43616e6e6f7420696e6372656173652070726f6a656374206d617820696e766f60448201527f636174696f6e732061626f766520636f726520636f6e7472616374207365742060648201527670726f6a656374206d617820696e766f636174696f6e7360481b608482015260a4016106e2565b80841015610e175760405162461bcd60e51b815260206004820152604360248201527f43616e6e6f74207365742070726f6a656374206d617820696e766f636174696f60448201527f6e7320746f206c657373207468616e2063757272656e7420696e766f636174696064820152626f6e7360e81b608482015260a4016106e2565b60008581526001602052604090819020805460ff1962ffffff8816600160301b021668ffffff0000000000ff19909116178387141790555185907f8445d32a2ee05956c6c842357ca16ee41e92657b1cbcbf1c94f500672e48c3b190610e809087815260200190565b60405180910390a25050505050565b6000610b6a610e9d83610ebf565b85908590611759565b6000610eb6858585856000610692565b95945050505050565b6040516001600160601b0319606083901b166020820152600090603401604051602081830303815290604052805190602001209050919050565b60405163a47d29cb60e01b81526004810182905281907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a47d29cb90602401602060405180830381865afa158015610f60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f849190611fd6565b6001600160a01b0316336001600160a01b031614610fb45760405162461bcd60e51b81526004016106e290611ff3565b60405162461bcd60e51b81526020600482015260146024820152731058dd1a5bdb881b9bdd081cdd5c1c1bdc9d195960621b60448201526064016106e2565b600080600061100185610b72565b905080156110405760008581526003602090815260408083206001600160a01b0388168452909152902054600193508082111561103e5780820392505b505b509250929050565b60405163a47d29cb60e01b81526004810183905282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a47d29cb90602401602060405180830381865afa1580156110af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d39190611fd6565b6001600160a01b0316336001600160a01b0316146111035760405162461bcd60e51b81526004016106e290611ff3565b6000838152600160208190526040808320918201859055815461ff0019166101001790915551839185917f26118a27aca826f829f3bfe21b140b4455c00b434849bd0da50d1e1a9720fb5c9190a3505050565b600084815260026020526040812054610eb69061117284610ebf565b869186916117a5565b60405162461bcd60e51b815260206004820152601960248201527826bab9ba10383937bb34b2329026b2b935b63290383937b7b360391b60448201526000906064016106e2565b60006111d18686868686610692565b9695505050505050565b60405163a47d29cb60e01b81526004810183905282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a47d29cb90602401602060405180830381865afa158015611242573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112669190611fd6565b6001600160a01b0316336001600160a01b0316146112965760405162461bcd60e51b81526004016106e290611ff3565b600083815260016020819052604091829020805462ffffff861663010000000265ffffffff0000199091161762010000178155915185917fc594e23b9382359e253cdaba84b0aefe5ad09ccfd5706891673743b37229c2d29161132091797573654d61784d696e7473506572416464724f7665727269646560301b82521515602082015260400190565b60405180910390a260408051766d61784d696e7473506572416464724f7665727269646560481b815262ffffff8516602082015285917f8a5820ca7148caeda3176e8d60a3bb413b4bcca45922ac2178b1ee191fe320eb91015b60405180910390a250505050565b60405163a47d29cb60e01b81526004810183905282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a47d29cb90602401602060405180830381865afa1580156113ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114139190611fd6565b6001600160a01b0316336001600160a01b0316146114435760405162461bcd60e51b81526004016106e290611ff3565b816114885760405162461bcd60e51b8152602060048201526015602482015274149bdbdd081b5d5cdd081899481c1c9bdd9a591959605a1b60448201526064016106e2565b6000838152600260209081526040918290208490558151691b595c9adb19549bdbdd60b21b815290810184905284917f729c0261c7e3c976b9a392f2bb08064d7a03c82f9f1473ab0ee284c985df0815910160405180910390a2505050565b60405163a47d29cb60e01b81526004810182905281907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a47d29cb90602401602060405180830381865afa15801561154e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115729190611fd6565b6001600160a01b0316336001600160a01b0316146115a25760405162461bcd60e51b81526004016106e290611ff3565b604051630ea5613f60e01b81526004810183905260009081906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690630ea5613f9060240160c060405180830381865afa15801561160c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116309190612018565b50505060008781526001602052604090819020805460ff1962ffffff8616600160301b021668ffffff0000000000ff1990911617858514179055519194509192508591507f8445d32a2ee05956c6c842357ca16ee41e92657b1cbcbf1c94f500672e48c3b19061137a9085815260200190565b3415611754576000806116b6843461208d565b905080156117465760405133908290600081818185875af1925050503d80600081146116fe576040519150601f19603f3d011682016040523d82523d6000602084013e611703565b606091505b505080925050816117465760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b60448201526064016106e2565b6117518585856117bd565b50505b505050565b600081815b8481101561179c576117888287878481811061177c5761177c6120a0565b90506020020135611bae565b915080611794816120b6565b91505061175e565b50949350505050565b6000826117b3868685611759565b1495945050505050565b600082116117ca57505050565b60008060008060008060007f00000000000000000000000000000000000000000000000000000000000000001561193557604051638639415b60e01b8152600481018b9052602481018a905260009081906001600160a01b038b1690638639415b9060440161010060405180830381865afa15801561184d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187191906120cf565b969e50949c50909a5098509196509194509092509050811561192e576040516001600160a01b038216908390600081818185875af1925050503d80600081146118d6576040519150601f19603f3d011682016040523d82523d6000602084013e6118db565b606091505b5050809950508861192e5760405162461bcd60e51b815260206004820181905260248201527f506c6174666f726d2050726f7669646572207061796d656e74206661696c656460448201526064016106e2565b50506119b5565b604051638639415b60e01b8152600481018b9052602481018a90526001600160a01b03891690638639415b9060440160c060405180830381865afa158015611981573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a59190612157565b949a509298509096509450925090505b8515611a5c576040516001600160a01b038616908790600081818185875af1925050503d8060008114611a04576040519150601f19603f3d011682016040523d82523d6000602084013e611a09565b606091505b50508097505086611a5c5760405162461bcd60e51b815260206004820152601e60248201527f52656e6465722050726f7669646572207061796d656e74206661696c6564000060448201526064016106e2565b8315611afb576040516001600160a01b038416908590600081818185875af1925050503d8060008114611aab576040519150601f19603f3d011682016040523d82523d6000602084013e611ab0565b606091505b50508097505086611afb5760405162461bcd60e51b8152602060048201526015602482015274105c9d1a5cdd081c185e5b595b9d0819985a5b1959605a1b60448201526064016106e2565b8115611ba2576040516001600160a01b038216908390600081818185875af1925050503d8060008114611b4a576040519150601f19603f3d011682016040523d82523d6000602084013e611b4f565b606091505b50508097505086611ba25760405162461bcd60e51b815260206004820152601f60248201527f4164646974696f6e616c205061796565207061796d656e74206661696c65640060448201526064016106e2565b50505050505050505050565b6000818310611bca576000828152602084905260409020611bd9565b60008381526020839052604090205b90505b92915050565b6001600160a01b0381168114611bf757600080fd5b50565b60008083601f840112611c0c57600080fd5b5081356001600160401b03811115611c2357600080fd5b6020830191508360208260051b8501011115611c3e57600080fd5b9250929050565b600080600080600060808688031215611c5d57600080fd5b8535611c6881611be2565b94506020860135935060408601356001600160401b03811115611c8a57600080fd5b611c9688828901611bfa565b9094509250506060860135611caa81611be2565b809150509295509295909350565b600080600060408486031215611ccd57600080fd5b8335925060208401356001600160401b03811115611cea57600080fd5b611cf686828701611bfa565b9497909650939450505050565b600060208284031215611d1557600080fd5b5035919050565b60008060408385031215611d2f57600080fd5b50508035926020909101359150565b600080600060408486031215611d5357600080fd5b83356001600160401b03811115611d6957600080fd5b611d7586828701611bfa565b9094509250506020840135611d8981611be2565b809150509250925092565b60008060008060608587031215611daa57600080fd5b8435611db581611be2565b93506020850135925060408501356001600160401b03811115611dd757600080fd5b611de387828801611bfa565b95989497509550505050565b600060208284031215611e0157600080fd5b8135611e0c81611be2565b9392505050565b60008060408385031215611e2657600080fd5b823591506020830135611e3881611be2565b809150509250929050565b60008060008060608587031215611e5957600080fd5b8435935060208501356001600160401b03811115611e7657600080fd5b611e8287828801611bfa565b9094509250506040850135611e9681611be2565b939692955090935050565b6000815180845260005b81811015611ec757602081850181015186830182015201611eab565b506000602082860101526020601f19601f83011685010191505092915050565b8415158152836020820152608060408201526000611f086080830185611ea1565b905060018060a01b038316606083015295945050505050565b60008060408385031215611f3457600080fd5b8235611f3f81611be2565b946020939093013593505050565b60008060408385031215611f6057600080fd5b82359150602083013562ffffff81168114611e3857600080fd5b602081526000611bd96020830184611ea1565b80518015158114611f9d57600080fd5b919050565b600060208284031215611fb457600080fd5b611bd982611f8d565b600060208284031215611fcf57600080fd5b5051919050565b600060208284031215611fe857600080fd5b8151611e0c81611be2565b6020808252600b908201526a13db9b1e48105c9d1a5cdd60aa1b604082015260600190565b60008060008060008060c0878903121561203157600080fd5b865195506020870151945061204860408801611f8d565b935061205660608801611f8d565b92506080870151915061206b60a08801611f8d565b90509295509295509295565b634e487b7160e01b600052601160045260246000fd5b81810381811115611bdc57611bdc612077565b634e487b7160e01b600052603260045260246000fd5b6000600182016120c8576120c8612077565b5060010190565b600080600080600080600080610100898b0312156120ec57600080fd5b8851975060208901516120fe81611be2565b60408a015160608b0151919850965061211681611be2565b60808a015160a08b0151919650945061212e81611be2565b60c08a015160e08b0151919450925061214681611be2565b809150509295985092959890939650565b60008060008060008060c0878903121561217057600080fd5b86519550602087015161218281611be2565b60408801516060890151919650945061219a81611be2565b608088015160a089015191945092506121b281611be2565b80915050929550929550929556fea264697066735822122060238e12b24e2acb196ee14d37a8d59816754d93c2374a2712a9f9629a6f615d64736f6c63430008110033000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7070000000000000000000000006e522449c1642e7cb0b12a2889ccbf79b51c69f800000000000000000000000000000000000076a84fef008cdabe6409d2fe638b

Deployed Bytecode

0x6080604052600436106101695760003560e01c806392a10f83116100c757806392a10f83146103c95780639775f86f14610415578063aea7d3b91461042a578063bf5bf5f814610462578063c15127c014610496578063c71b1b71146104a9578063cc3352bf14610548578063d684ec6714610568578063da7e7c5014610194578063db6921b214610595578063dd85582f146105c9578063e9d1e8ac146105fd578063efef39a114610644578063f1e3311514610652578063f7bd4b881461067257600080fd5b806157b61461016e5780615aa11461019457806315bed159146101a75780631607c995146101c75780631ec2e523146101e9578063202c5805146102095780633aa5fe591461021c57806340d1397e1461023c578063462add461461025c57806356690aaf1461029c5780636b453e70146102d55780636cb9b7ff1461030c5780636ede4ade1461032c578063774159c61461034c578063891407c0146103b6575b600080fd5b61018161017c366004611c45565b610692565b6040519081526020015b60405180910390f35b6101816101a2366004611cb8565b610b5a565b3480156101b357600080fd5b506101816101c2366004611d03565b610b72565b3480156101d357600080fd5b506101e76101e2366004611d1c565b610bac565b005b3480156101f557600080fd5b50610181610204366004611d3e565b610e8f565b610181610217366004611d94565b610ea6565b34801561022857600080fd5b50610181610237366004611def565b610ebf565b34801561024857600080fd5b506101e7610257366004611d03565b610ef9565b34801561026857600080fd5b5061028c610277366004611d03565b60009081526001602052604090205460ff1690565b604051901515815260200161018b565b3480156102a857600080fd5b506101816102b7366004611d03565b600090815260016020526040902054600160301b900462ffffff1690565b3480156102e157600080fd5b506102f56102f0366004611e13565b610ff3565b60408051921515835260208301919091520161018b565b34801561031857600080fd5b506101e7610327366004611d1c565b611048565b34801561033857600080fd5b5061028c610347366004611e43565b611156565b34801561035857600080fd5b506103a6610367366004611d03565b600090815260016020818152604080842080549301548151808301909252600382526208aa8960eb1b9282019290925261010090920460ff1693909290565b60405161018b9493929190611ee7565b6101816103c4366004611f21565b61117b565b3480156103d557600080fd5b506103fd7f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb70781565b6040516001600160a01b03909116815260200161018b565b34801561042157600080fd5b50610181600181565b34801561043657600080fd5b50610181610445366004611e13565b600360209081526000928352604080842090915290825290205481565b34801561046e57600080fd5b506103fd7f00000000000000000000000000000000000076a84fef008cdabe6409d2fe638b81565b6101816104a4366004611c45565b6111c2565b3480156104b557600080fd5b5061050c6104c4366004611d03565b6001602081905260009182526040909120805491015460ff8083169261010081048216926201000082049092169162ffffff63010000008304811692600160301b9004169086565b60408051961515875294151560208701529215159385019390935262ffffff9081166060850152909116608083015260a082015260c00161018b565b34801561055457600080fd5b506101e7610563366004611f4d565b6111db565b34801561057457600080fd5b50610181610583366004611d03565b60026020526000908152604090205481565b3480156105a157600080fd5b5061028c7f000000000000000000000000000000000000000000000000000000000000000181565b3480156105d557600080fd5b506103fd7f0000000000000000000000006e522449c1642e7cb0b12a2889ccbf79b51c69f881565b34801561060957600080fd5b506106376040518060400160405280600e81526020016d4d696e7465724d65726b6c65563560901b81525081565b60405161018b9190611f7a565b6101816103c4366004611d03565b34801561065e57600080fd5b506101e761066d366004611d1c565b611388565b34801561067e57600080fd5b506101e761068d366004611d03565b6114e7565b60006002600054036106eb5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60026000908155858152600160205260409020805460ff161561075e5760405162461bcd60e51b815260206004820152602560248201527f4d6178696d756d206e756d626572206f6620696e766f636174696f6e732072656044820152641858da195960da1b60648201526084016106e2565b6001810154348111156107b35760405162461bcd60e51b815260206004820181905260248201527f4d7573742073656e64206d696e696d756d2076616c756520746f206d696e742160448201526064016106e2565b8154610100900460ff166108005760405162461bcd60e51b8152602060048201526014602482015273141c9a58d9481b9bdd0818dbdb999a59dd5c995960621b60448201526064016106e2565b336001600160a01b038516156109215760405163090c9a2d60e41b81523360048201526001600160a01b0386811660248301527f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb707811660448301526000917f00000000000000000000000000000000000076a84fef008cdabe6409d2fe638b909116906390c9a2d090606401602060405180830381865afa1580156108a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108cd9190611fa2565b90508061091c5760405162461bcd60e51b815260206004820152601e60248201527f496e76616c69642064656c65676174652d7661756c742070616972696e67000060448201526064016106e2565b859150505b61092d88888884611156565b6109705760405162461bcd60e51b815260206004820152601460248201527324b73b30b634b21026b2b935b63290383937b7b360611b60448201526064016106e2565b825460009062010000900460ff16610989576001610998565b83546301000000900462ffffff165b60008a81526003602090815260408083206001600160a01b03871684529091529020549091508111806109c9575080155b610a2f5760405162461bcd60e51b815260206004820152603160248201527f4d6178696d756d206e756d626572206f6620696e766f636174696f6e732070656044820152701c881859191c995cdcc81c995858da1959607a1b60648201526084016106e2565b60008981526003602090815260408083206001600160a01b0386811680865291909352928190208054600101905551630d4d151360e01b81528c82166004820152602481018c905260448101929092527f0000000000000000000000006e522449c1642e7cb0b12a2889ccbf79b51c69f81690630d4d1513906064016020604051808303816000875af1158015610aca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aee9190611fbd565b845490955060001962ffffff600160301b90920482160116620f4240860603610b1d57835460ff191660011784555b610b4889847f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7076116a3565b50505050600160005595945050505050565b6000610b6a338585856000610692565b949350505050565b6000818152600160205260408120805462010000900460ff1615610ba357546301000000900462ffffff1692915050565b50600192915050565b60405163a47d29cb60e01b81526004810183905282907f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7076001600160a01b03169063a47d29cb90602401602060405180830381865afa158015610c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c379190611fd6565b6001600160a01b0316336001600160a01b031614610c675760405162461bcd60e51b81526004016106e290611ff3565b604051630ea5613f60e01b81526004810184905260009081906001600160a01b037f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7071690630ea5613f9060240160c060405180830381865afa158015610cd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf59190612018565b5092955092935050505081841115610d955760405162461bcd60e51b815260206004820152605760248201527f43616e6e6f7420696e6372656173652070726f6a656374206d617820696e766f60448201527f636174696f6e732061626f766520636f726520636f6e7472616374207365742060648201527670726f6a656374206d617820696e766f636174696f6e7360481b608482015260a4016106e2565b80841015610e175760405162461bcd60e51b815260206004820152604360248201527f43616e6e6f74207365742070726f6a656374206d617820696e766f636174696f60448201527f6e7320746f206c657373207468616e2063757272656e7420696e766f636174696064820152626f6e7360e81b608482015260a4016106e2565b60008581526001602052604090819020805460ff1962ffffff8816600160301b021668ffffff0000000000ff19909116178387141790555185907f8445d32a2ee05956c6c842357ca16ee41e92657b1cbcbf1c94f500672e48c3b190610e809087815260200190565b60405180910390a25050505050565b6000610b6a610e9d83610ebf565b85908590611759565b6000610eb6858585856000610692565b95945050505050565b6040516001600160601b0319606083901b166020820152600090603401604051602081830303815290604052805190602001209050919050565b60405163a47d29cb60e01b81526004810182905281907f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7076001600160a01b03169063a47d29cb90602401602060405180830381865afa158015610f60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f849190611fd6565b6001600160a01b0316336001600160a01b031614610fb45760405162461bcd60e51b81526004016106e290611ff3565b60405162461bcd60e51b81526020600482015260146024820152731058dd1a5bdb881b9bdd081cdd5c1c1bdc9d195960621b60448201526064016106e2565b600080600061100185610b72565b905080156110405760008581526003602090815260408083206001600160a01b0388168452909152902054600193508082111561103e5780820392505b505b509250929050565b60405163a47d29cb60e01b81526004810183905282907f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7076001600160a01b03169063a47d29cb90602401602060405180830381865afa1580156110af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d39190611fd6565b6001600160a01b0316336001600160a01b0316146111035760405162461bcd60e51b81526004016106e290611ff3565b6000838152600160208190526040808320918201859055815461ff0019166101001790915551839185917f26118a27aca826f829f3bfe21b140b4455c00b434849bd0da50d1e1a9720fb5c9190a3505050565b600084815260026020526040812054610eb69061117284610ebf565b869186916117a5565b60405162461bcd60e51b815260206004820152601960248201527826bab9ba10383937bb34b2329026b2b935b63290383937b7b360391b60448201526000906064016106e2565b60006111d18686868686610692565b9695505050505050565b60405163a47d29cb60e01b81526004810183905282907f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7076001600160a01b03169063a47d29cb90602401602060405180830381865afa158015611242573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112669190611fd6565b6001600160a01b0316336001600160a01b0316146112965760405162461bcd60e51b81526004016106e290611ff3565b600083815260016020819052604091829020805462ffffff861663010000000265ffffffff0000199091161762010000178155915185917fc594e23b9382359e253cdaba84b0aefe5ad09ccfd5706891673743b37229c2d29161132091797573654d61784d696e7473506572416464724f7665727269646560301b82521515602082015260400190565b60405180910390a260408051766d61784d696e7473506572416464724f7665727269646560481b815262ffffff8516602082015285917f8a5820ca7148caeda3176e8d60a3bb413b4bcca45922ac2178b1ee191fe320eb91015b60405180910390a250505050565b60405163a47d29cb60e01b81526004810183905282907f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7076001600160a01b03169063a47d29cb90602401602060405180830381865afa1580156113ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114139190611fd6565b6001600160a01b0316336001600160a01b0316146114435760405162461bcd60e51b81526004016106e290611ff3565b816114885760405162461bcd60e51b8152602060048201526015602482015274149bdbdd081b5d5cdd081899481c1c9bdd9a591959605a1b60448201526064016106e2565b6000838152600260209081526040918290208490558151691b595c9adb19549bdbdd60b21b815290810184905284917f729c0261c7e3c976b9a392f2bb08064d7a03c82f9f1473ab0ee284c985df0815910160405180910390a2505050565b60405163a47d29cb60e01b81526004810182905281907f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7076001600160a01b03169063a47d29cb90602401602060405180830381865afa15801561154e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115729190611fd6565b6001600160a01b0316336001600160a01b0316146115a25760405162461bcd60e51b81526004016106e290611ff3565b604051630ea5613f60e01b81526004810183905260009081906001600160a01b037f000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7071690630ea5613f9060240160c060405180830381865afa15801561160c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116309190612018565b50505060008781526001602052604090819020805460ff1962ffffff8616600160301b021668ffffff0000000000ff1990911617858514179055519194509192508591507f8445d32a2ee05956c6c842357ca16ee41e92657b1cbcbf1c94f500672e48c3b19061137a9085815260200190565b3415611754576000806116b6843461208d565b905080156117465760405133908290600081818185875af1925050503d80600081146116fe576040519150601f19603f3d011682016040523d82523d6000602084013e611703565b606091505b505080925050816117465760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b60448201526064016106e2565b6117518585856117bd565b50505b505050565b600081815b8481101561179c576117888287878481811061177c5761177c6120a0565b90506020020135611bae565b915080611794816120b6565b91505061175e565b50949350505050565b6000826117b3868685611759565b1495945050505050565b600082116117ca57505050565b60008060008060008060007f00000000000000000000000000000000000000000000000000000000000000011561193557604051638639415b60e01b8152600481018b9052602481018a905260009081906001600160a01b038b1690638639415b9060440161010060405180830381865afa15801561184d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187191906120cf565b969e50949c50909a5098509196509194509092509050811561192e576040516001600160a01b038216908390600081818185875af1925050503d80600081146118d6576040519150601f19603f3d011682016040523d82523d6000602084013e6118db565b606091505b5050809950508861192e5760405162461bcd60e51b815260206004820181905260248201527f506c6174666f726d2050726f7669646572207061796d656e74206661696c656460448201526064016106e2565b50506119b5565b604051638639415b60e01b8152600481018b9052602481018a90526001600160a01b03891690638639415b9060440160c060405180830381865afa158015611981573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a59190612157565b949a509298509096509450925090505b8515611a5c576040516001600160a01b038616908790600081818185875af1925050503d8060008114611a04576040519150601f19603f3d011682016040523d82523d6000602084013e611a09565b606091505b50508097505086611a5c5760405162461bcd60e51b815260206004820152601e60248201527f52656e6465722050726f7669646572207061796d656e74206661696c6564000060448201526064016106e2565b8315611afb576040516001600160a01b038416908590600081818185875af1925050503d8060008114611aab576040519150601f19603f3d011682016040523d82523d6000602084013e611ab0565b606091505b50508097505086611afb5760405162461bcd60e51b8152602060048201526015602482015274105c9d1a5cdd081c185e5b595b9d0819985a5b1959605a1b60448201526064016106e2565b8115611ba2576040516001600160a01b038216908390600081818185875af1925050503d8060008114611b4a576040519150601f19603f3d011682016040523d82523d6000602084013e611b4f565b606091505b50508097505086611ba25760405162461bcd60e51b815260206004820152601f60248201527f4164646974696f6e616c205061796565207061796d656e74206661696c65640060448201526064016106e2565b50505050505050505050565b6000818310611bca576000828152602084905260409020611bd9565b60008381526020839052604090205b90505b92915050565b6001600160a01b0381168114611bf757600080fd5b50565b60008083601f840112611c0c57600080fd5b5081356001600160401b03811115611c2357600080fd5b6020830191508360208260051b8501011115611c3e57600080fd5b9250929050565b600080600080600060808688031215611c5d57600080fd5b8535611c6881611be2565b94506020860135935060408601356001600160401b03811115611c8a57600080fd5b611c9688828901611bfa565b9094509250506060860135611caa81611be2565b809150509295509295909350565b600080600060408486031215611ccd57600080fd5b8335925060208401356001600160401b03811115611cea57600080fd5b611cf686828701611bfa565b9497909650939450505050565b600060208284031215611d1557600080fd5b5035919050565b60008060408385031215611d2f57600080fd5b50508035926020909101359150565b600080600060408486031215611d5357600080fd5b83356001600160401b03811115611d6957600080fd5b611d7586828701611bfa565b9094509250506020840135611d8981611be2565b809150509250925092565b60008060008060608587031215611daa57600080fd5b8435611db581611be2565b93506020850135925060408501356001600160401b03811115611dd757600080fd5b611de387828801611bfa565b95989497509550505050565b600060208284031215611e0157600080fd5b8135611e0c81611be2565b9392505050565b60008060408385031215611e2657600080fd5b823591506020830135611e3881611be2565b809150509250929050565b60008060008060608587031215611e5957600080fd5b8435935060208501356001600160401b03811115611e7657600080fd5b611e8287828801611bfa565b9094509250506040850135611e9681611be2565b939692955090935050565b6000815180845260005b81811015611ec757602081850181015186830182015201611eab565b506000602082860101526020601f19601f83011685010191505092915050565b8415158152836020820152608060408201526000611f086080830185611ea1565b905060018060a01b038316606083015295945050505050565b60008060408385031215611f3457600080fd5b8235611f3f81611be2565b946020939093013593505050565b60008060408385031215611f6057600080fd5b82359150602083013562ffffff81168114611e3857600080fd5b602081526000611bd96020830184611ea1565b80518015158114611f9d57600080fd5b919050565b600060208284031215611fb457600080fd5b611bd982611f8d565b600060208284031215611fcf57600080fd5b5051919050565b600060208284031215611fe857600080fd5b8151611e0c81611be2565b6020808252600b908201526a13db9b1e48105c9d1a5cdd60aa1b604082015260600190565b60008060008060008060c0878903121561203157600080fd5b865195506020870151945061204860408801611f8d565b935061205660608801611f8d565b92506080870151915061206b60a08801611f8d565b90509295509295509295565b634e487b7160e01b600052601160045260246000fd5b81810381811115611bdc57611bdc612077565b634e487b7160e01b600052603260045260246000fd5b6000600182016120c8576120c8612077565b5060010190565b600080600080600080600080610100898b0312156120ec57600080fd5b8851975060208901516120fe81611be2565b60408a015160608b0151919850965061211681611be2565b60808a015160a08b0151919650945061212e81611be2565b60c08a015160e08b0151919450925061214681611be2565b809150509295985092959890939650565b60008060008060008060c0878903121561217057600080fd5b86519550602087015161218281611be2565b60408801516060890151919650945061219a81611be2565b608088015160a089015191945092506121b281611be2565b80915050929550929550929556fea264697066735822122060238e12b24e2acb196ee14d37a8d59816754d93c2374a2712a9f9629a6f615d64736f6c63430008110033

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

000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb7070000000000000000000000006e522449c1642e7cb0b12a2889ccbf79b51c69f800000000000000000000000000000000000076a84fef008cdabe6409d2fe638b

-----Decoded View---------------
Arg [0] : _genArt721Address (address): 0x145789247973C5D612bF121e9E4Eef84b63Eb707
Arg [1] : _minterFilter (address): 0x6E522449C1642E7cB0B12a2889CcBf79b51C69f8
Arg [2] : _delegationRegistryAddress (address): 0x00000000000076A84feF008CDAbe6409d2FE638B

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000145789247973c5d612bf121e9e4eef84b63eb707
Arg [1] : 0000000000000000000000006e522449c1642e7cb0b12a2889ccbf79b51c69f8
Arg [2] : 00000000000000000000000000000000000076a84fef008cdabe6409d2fe638b


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  ]
[ 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.