ETH Price: $3,189.90 (+1.07%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Team Mint191420732024-02-02 17:02:59362 days ago1706893379IN
0x334054Ff...C97F3A173
0 ETH0.0198872923.02561703
Purchase MMM191017162024-01-28 1:18:11368 days ago1706404691IN
0x334054Ff...C97F3A173
0.4 ETH0.0063302711.35585681
Purchase MMM190999452024-01-27 19:17:35368 days ago1706383055IN
0x334054Ff...C97F3A173
0.04 ETH0.0024980611.25645574
Purchase MMM190919772024-01-26 16:30:11369 days ago1706286611IN
0x334054Ff...C97F3A173
0.04 ETH0.0048413721.81555755
Purchase MMM190360972024-01-18 20:21:47377 days ago1705609307IN
0x334054Ff...C97F3A173
0.12 ETH0.0110563436.13268604
Purchase MMM190068492024-01-14 18:15:11381 days ago1705256111IN
0x334054Ff...C97F3A173
0.4 ETH0.0123904922.22725745
Purchase MMM189984072024-01-13 13:56:11382 days ago1705154171IN
0x334054Ff...C97F3A173
0.4 ETH0.0101676918.24018489
Purchase MMM189977792024-01-13 11:49:59382 days ago1705146599IN
0x334054Ff...C97F3A173
0.36 ETH0.0074458515.28160082
Purchase MMM189963952024-01-13 7:11:11383 days ago1705129871IN
0x334054Ff...C97F3A173
0.08 ETH0.0044085615.22285722
Purchase MMM189945942024-01-13 1:08:35383 days ago1705108115IN
0x334054Ff...C97F3A173
0.04 ETH0.0038199715.98082189
Purchase MMM189931482024-01-12 20:17:35383 days ago1705090655IN
0x334054Ff...C97F3A173
0.04 ETH0.0046111820.7771974
Purchase MMM189914812024-01-12 14:41:59383 days ago1705070519IN
0x334054Ff...C97F3A173
0.2 ETH0.009842525.23466608
Purchase MMM189911942024-01-12 13:43:59383 days ago1705067039IN
0x334054Ff...C97F3A173
0.04 ETH0.0067081426.18987286
Purchase MMM189910302024-01-12 13:11:11383 days ago1705065071IN
0x334054Ff...C97F3A173
0.04 ETH0.00598923.38221539
Purchase MMM189906972024-01-12 12:04:23383 days ago1705061063IN
0x334054Ff...C97F3A173
0.04 ETH0.0054683721.34957532
Purchase MMM189879262024-01-12 2:45:23384 days ago1705027523IN
0x334054Ff...C97F3A173
0.16 ETH0.0062713517.58790778
Purchase MMM189879142024-01-12 2:42:59384 days ago1705027379IN
0x334054Ff...C97F3A173
0.04 ETH0.0036830116.59501717
Purchase MMM189878982024-01-12 2:39:47384 days ago1705027187IN
0x334054Ff...C97F3A173
0.04 ETH0.0042272617.68469276
Purchase MMM189690702024-01-09 11:20:47386 days ago1704799247IN
0x334054Ff...C97F3A173
0.04 ETH0.0033713414.25519924
Purchase MMM189663822024-01-09 2:15:35387 days ago1704766535IN
0x334054Ff...C97F3A173
0.04 ETH0.0038213514.91931573
Purchase MMM189653822024-01-08 22:52:59387 days ago1704754379IN
0x334054Ff...C97F3A173
0.04 ETH0.0049364819.27387164
Purchase MMM189653562024-01-08 22:47:47387 days ago1704754067IN
0x334054Ff...C97F3A173
0.04 ETH0.0058539522.85497182
Purchase MMM189649402024-01-08 21:23:59387 days ago1704749039IN
0x334054Ff...C97F3A173
0.04 ETH0.0078036630.46700639
Purchase MMM189631542024-01-08 15:22:59387 days ago1704727379IN
0x334054Ff...C97F3A173
0.08 ETH0.0063704124.94154471
Purchase MMM189631182024-01-08 15:15:35387 days ago1704726935IN
0x334054Ff...C97F3A173
0.08 ETH0.0057211119.75428263
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
191017162024-01-28 1:18:11368 days ago1706404691
0x334054Ff...C97F3A173
0.108 ETH
191017162024-01-28 1:18:11368 days ago1706404691
0x334054Ff...C97F3A173
0.252 ETH
191017162024-01-28 1:18:11368 days ago1706404691
0x334054Ff...C97F3A173
0.04 ETH
190999452024-01-27 19:17:35368 days ago1706383055
0x334054Ff...C97F3A173
0.0108 ETH
190999452024-01-27 19:17:35368 days ago1706383055
0x334054Ff...C97F3A173
0.0252 ETH
190999452024-01-27 19:17:35368 days ago1706383055
0x334054Ff...C97F3A173
0.004 ETH
190919772024-01-26 16:30:11369 days ago1706286611
0x334054Ff...C97F3A173
0.0108 ETH
190919772024-01-26 16:30:11369 days ago1706286611
0x334054Ff...C97F3A173
0.0252 ETH
190919772024-01-26 16:30:11369 days ago1706286611
0x334054Ff...C97F3A173
0.004 ETH
190360972024-01-18 20:21:47377 days ago1705609307
0x334054Ff...C97F3A173
0.0324 ETH
190360972024-01-18 20:21:47377 days ago1705609307
0x334054Ff...C97F3A173
0.0756 ETH
190360972024-01-18 20:21:47377 days ago1705609307
0x334054Ff...C97F3A173
0.012 ETH
190068492024-01-14 18:15:11381 days ago1705256111
0x334054Ff...C97F3A173
0.108 ETH
190068492024-01-14 18:15:11381 days ago1705256111
0x334054Ff...C97F3A173
0.252 ETH
190068492024-01-14 18:15:11381 days ago1705256111
0x334054Ff...C97F3A173
0.04 ETH
189984072024-01-13 13:56:11382 days ago1705154171
0x334054Ff...C97F3A173
0.108 ETH
189984072024-01-13 13:56:11382 days ago1705154171
0x334054Ff...C97F3A173
0.252 ETH
189984072024-01-13 13:56:11382 days ago1705154171
0x334054Ff...C97F3A173
0.04 ETH
189977792024-01-13 11:49:59382 days ago1705146599
0x334054Ff...C97F3A173
0.0972 ETH
189977792024-01-13 11:49:59382 days ago1705146599
0x334054Ff...C97F3A173
0.2268 ETH
189977792024-01-13 11:49:59382 days ago1705146599
0x334054Ff...C97F3A173
0.036 ETH
189963952024-01-13 7:11:11383 days ago1705129871
0x334054Ff...C97F3A173
0.0216 ETH
189963952024-01-13 7:11:11383 days ago1705129871
0x334054Ff...C97F3A173
0.0504 ETH
189963952024-01-13 7:11:11383 days ago1705129871
0x334054Ff...C97F3A173
0.008 ETH
189945942024-01-13 1:08:35383 days ago1705108115
0x334054Ff...C97F3A173
0.0108 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MultiMerkleMinterV1

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion
File 1 of 21 : MultiMerkleMinterV1.sol
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.17;

// Created By: @backseats_eth

// Forked from and inspired by ArtBlocks' MerkleMinterV1 contract
// https://github.com/ArtBlocks/artblocks-contracts/blob/32738da594e7b9d18e25011b1c7fefa4abb1bda9/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV1.sol

import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";

import "./interfaces/IGenArt721CoreContractV3_Base.sol";
import "./interfaces/IFilteredMinterMerkleV2.sol";
import "./interfaces/IMinterFilterV0.sol";
import "./MinterBase_v0_1_1.sol";

/**
 * @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.
 * @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:
 * - createNewStage
 * - manuallyLimitProjectMaxInvocations
 * - setProjectMaxInvocations
 * - teamMint
 * - updateMerkleRoot
 * - updatePricePerTokenInWei
 * - updateSystemAddress
 * ----------------------------------------------------------------------------
 * Additional admin and artist privileged roles may be described on other
 * contracts that this minter integrates with.
 * ----------------------------------------------------------------------------
 */
contract MultiMerkleMinterV1 is ReentrancyGuard, MinterBase, IFilteredMinterMerkleV2 {
    using MerkleProof for bytes32[];
    using ECDSA for bytes32;

    // Used for unneeded protocol-conformance functions
    error ActionNotSupported();

    /*//////////////////////////////////////////////////////////////
                                STORAGE
    //////////////////////////////////////////////////////////////*/

    /// 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 = "MultiMerkleMinterV1";

    /// The theoretical total number of tokens that can be minted for a project on GenArt721Core
    uint256 constant ONE_MILLION = 1_000_000;

    // A mapping of project ids to their Project Config
    mapping(uint256 => ProjectConfig) public projectConfig;

    /*//////////////////////////////////////////////////////////////
                                STRUCTS
    //////////////////////////////////////////////////////////////*/

    // A ProjectConfig is specific to the MultiMerkleMinter so that it can be re-used to mint
    // multiple GenArtCoreV3-conforming projects
    struct ProjectConfig {
        // If the project has minted out
        bool maxHasBeenInvoked;

        // If the creator or artist has configured the price
        bool priceIsConfigured;

        // The id of the current stage of the mint. If 0, the project is minted out or not started
        uint8 currentStageId;

        // The count of the stages for the project. Starts at 0 for no Stages created
        uint8 stagesCount;

        // The maximum amount of tokens that can be minted for the project
        uint24 maxInvocations;

        // Which system address is the signer for the project id
        address systemAddress;

        // If a nonce has been used or not to mint the project
        mapping(string => bool) usedNonces;

        // How many times an address has minted for the project at this Stage
        mapping(uint8 => mapping(address => uint)) addressMintedCount;

        // A mapping of the id of the Stage to the Stage, sequentially from 1.
        mapping(uint8 => Stage) stages;
    }

    struct Stage {
        // The id of the Stage. Starts at 1. 0 is the null state
        uint8 id;

        // The maximum amount of tokens a wallet can mint in that stage
        uint8 transactionMaxInvocations;

        // The Unix timestamp of when the stage starts. Suffers from the 2038 problem but we'll be fine
        uint32 stageStartTime;

        // Optional Merkle Root. Use 0x if not used
        bytes32 merkleRoot;

        // The price of the token for that stage
        uint256 pricePerTokenInWei;
    }

    /*//////////////////////////////////////////////////////////////
                                MODIFIER
    //////////////////////////////////////////////////////////////*/

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

    /*//////////////////////////////////////////////////////////////
                                CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Initializes contract to be a Filtered Minter for
     * `_minterFilter`, integrated with Art Blocks core contract
     * at address `_genArt721Address`.
     * @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.
     */
    constructor(
        address _genArt721Address,
        address _minterFilter
    ) 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
        );

        minterFilterAddress = _minterFilter;
        minterFilter = IMinterFilterV0(_minterFilter);

        require(minterFilter.genArt721CoreAddress() == _genArt721Address, "Illegal Contract Pairing");
    }

    /**
     * @notice Update the Merkle root for project `_projectId`.
     * @param _projectId Project ID to be updated.
     * @param _stageId Stage ID to be updated.
     * @param _root root of Merkle tree defining addresses allowed to mint
     * on project `_projectId`.
     */
    function updateMerkleRoot(
        uint256 _projectId,
        uint8 _stageId,
        bytes32 _root
    ) external onlyArtist(_projectId) {
        ProjectConfig storage _projectConfig = projectConfig[_projectId];
        _projectConfig.stages[_stageId].merkleRoot = _root;
    }

    function updateStartingTimeForStage(
        uint256 _projectId,
        uint8 _stageId,
        uint32 _newStartingTime
    ) external onlyArtist(_projectId) {
        require(_stageId > 0, "Can't be 0");
        ProjectConfig storage _projectConfig = projectConfig[_projectId];
        require(_projectConfig.stagesCount > 0, "No Stages exist for project");
        require(_newStartingTime > 0, "Can't be 0");
        require(_newStartingTime > block.timestamp, "Must be in the future");
        require(_stageId <= _projectConfig.stagesCount, "Stage doesn't exist");

        if (_stageId == _projectConfig.stagesCount) {
            _projectConfig.stages[_stageId].stageStartTime = _newStartingTime;

        } else if (_stageId + 1 <= _projectConfig.stagesCount) {
            // Ensure future stage exists and it doesn't collide
            Stage memory futureStage = _projectConfig.stages[_stageId + 1];
            require(_newStartingTime < futureStage.stageStartTime, "Stage can't start after next one");

            _projectConfig.stages[_stageId].stageStartTime = _newStartingTime;
        }
    }

    /**
     * @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 _merkleRoot The Merkle root for the project.
     * @param _proof Merkle proof for address.
     * @param _address Addrexss to check.
     * @return inAllowlist true only if address is allowed to mint and valid
     * Merkle proof was provided
     */
    function verifyAddress(
        bytes32 _merkleRoot,
        bytes32[] calldata _proof,
        address _address
    ) public pure returns (bool) {
        return _proof.verifyCalldata(_merkleRoot, hashAddress(_address));
    }

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

    // Returns the number of tokens minted for an address for a project id
    function getAddressMintedCount(uint256 _projectId, uint8 _stageId, address _address) external view returns (uint) {
        return projectConfig[_projectId].addressMintedCount[_stageId][_address];
    }

    /**
     * @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,
        uint8 _stageId,
        uint256 _pricePerTokenInWei
    ) external onlyArtist(_projectId) {
        ProjectConfig storage _projectConfig = projectConfig[_projectId];
        _projectConfig.priceIsConfigured = true;
        _projectConfig.stages[_stageId].pricePerTokenInWei = _pricePerTokenInWei;
    }

    /**
    * @notice Purchases 1 or more tokens from a project
    * @param _projectId The project id
    * @param _amount The number of tokens to purchase
    * @param _signature A signature, generated from the minting site
    * @param _data abi-encoded data, generated from the minting site
    * @param _nonce A nonce, to accompany the signature
    */
    function purchaseMMM(
        uint256 _projectId,
        uint256 _amount,
        bytes calldata _signature,
        bytes calldata _data,
        bytes32[] calldata _merkleProof,
        string calldata _nonce
    ) public payable nonReentrant() {
        uint256 pid = _projectId;
        uint256 amount = _amount;
        bytes memory sig = _signature;

        // CHECKS
        ProjectConfig storage _projectConfig = projectConfig[pid];

        // 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, "Max Invocations Reached");

        // Gets the current Stage by timestamp
        Stage memory _currentStage = currentStage(pid);

        // If what we have set in storage is not the current Stage, update it
        if (_projectConfig.currentStageId != _currentStage.id) {
            _projectConfig.currentStageId = _currentStage.id;
        }

        // See function `createNewStage`
        require(_currentStage.stageStartTime != 0, "Create A Stage To Begin");

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

        // Check the price is correct
        uint256 expectedPrice = currentStagePrice(pid) * amount;
        require(msg.value == expectedPrice, "Wrong Price");

        // Ensure the nonce coming from the server hasn't already been used
        require(!_projectConfig.usedNonces[_nonce], "Nonce Used");

        // Ensure the data signed from the server is valid
        require(isValidSignature(
            _projectConfig.systemAddress, keccak256(abi.encodePacked(msg.sender, amount, _nonce)), sig),
            "Invalid Signature"
        );

        // Decode a tuple of the address and the max they can mint and ensure the data coming from the server is correct
        (address _address, uint256 maxMintCount) = abi.decode(_data, (address, uint256));
        require(_address == msg.sender, "Bad Data");

        // If server sends through a maxMintCount > 0, check how many they've minted this stage
        bool doMaxCheckForStage = maxMintCount > 0;

        // Check to see if the msg.sender is on the allowlist if the Stage has an allowlist. First checks to see if the merkle root is the 0 address
        if (address(uint160(uint256(currentStage(pid).merkleRoot))) != address(0)) {
            bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
            require(MerkleProof.verify(_merkleProof, _currentStage.merkleRoot, leaf), "Not On Allowlist");
        }

        // Ensure the amount they're minting doesn't exceed their max for the stage
        if (doMaxCheckForStage) {
            require(_projectConfig.addressMintedCount[_currentStage.id][msg.sender] + amount <= maxMintCount, "Can't Mint That Many");
        }

        // Update the record of the nonce and the overall msg.sender mint count
        _projectConfig.usedNonces[_nonce] = true;

        // Increment user's minted count for this project
        unchecked {
            _projectConfig.addressMintedCount[_currentStage.id][msg.sender] += amount;
        }

        // Mint tokens
        uint i;
        do {
            uint256 tokenId = minterFilter.mint(msg.sender, pid, msg.sender);

            // If the max has been reached, set the flag to true
            unchecked {
                if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
                    _projectConfig.maxHasBeenInvoked = true;
                    // If minted out, set this to 0.
                    _projectConfig.currentStageId = 0;
                }
            }

            unchecked { ++i; }
        } while (i < amount);

        // INTERACTIONS
        splitFundsETH(pid, msg.value, genArt721CoreAddress);
    }

    function teamMint(
        uint256 _projectId,
        address[] calldata _addresses,
        uint256[] calldata _counts
    ) external onlyArtist(_projectId) {
        require(_addresses.length == _counts.length, "Unequal arrays");

        uint256 length = _addresses.length;
        // Outer loop iterator
        uint256 i;
        // Inner loop iterator
        uint256 n;

        // Loops through the addresses
        do {
            // Loops through the tokens to mint the address
            do {
                // This will fail in the underlying contract if exceeds project invocations
                minterFilter.mint(_addresses[i], _projectId, msg.sender);
                unchecked { ++n; }
            } while (n < _counts[i]);

            unchecked { ++i; }
        } while (i < length);
    }

    function createNewStage(
        uint256 _projectId,
        uint32 _stageStartTime,
        bytes32 _merkleRoot,
        uint256 _pricePerTokenInWei,
        uint8 _transactionMaxInvocations
    ) onlyArtist(_projectId) external {
        // Ensure the Stage start time is in the future
        require(_stageStartTime > block.timestamp, "No Start Block In Past");

        // Get ProjectConfig from mapping
        ProjectConfig storage config = projectConfig[_projectId];

        // Don't create a new Stage if minted out
        require(!config.maxHasBeenInvoked, "Minted Out Cant Create New Stage");

        // Start the `stages` mapping IDs at 1 so that 0 can be a null state
        // Set the ProjectConfig's `priceIsConfigured` if this is the first Stage
        uint8 nextStageId;
        if (config.stagesCount == 0) {
             nextStageId = 1;
             config.priceIsConfigured = true;
        } else {
            nextStageId = config.stagesCount + 1;
        }

        // Increment the ProjectConfig's Stages count (i.e. 2 stages, stagesCount = 2)
        unchecked { ++config.stagesCount; }

        // Store the new Stage
        config.stages[nextStageId] = Stage({
            id: nextStageId,
            transactionMaxInvocations: _transactionMaxInvocations,
            stageStartTime: _stageStartTime,
            merkleRoot: _merkleRoot,
            pricePerTokenInWei: _pricePerTokenInWei
        });
    }

    // Returns an array of all of the Stages for a given Project id
    function allStages(uint256 _projectId) public view returns (Stage[] memory) {
        ProjectConfig storage config = projectConfig[_projectId];

        // i.e. 2 stages, length = 2
        uint8 length = config.stagesCount;
        Stage[] memory stages = new Stage[](length);

        for(uint8 i; i < length;) {
            // Add 1 to `i` here because Stages start at 1; 0 is the null state
            stages[i] = getStage(_projectId, i + 1);
            unchecked { ++i; }
        }

        return stages;
    }

    function getStage(uint256 _productId, uint8 _stageId) public view returns (Stage memory) {
        return projectConfig[_productId].stages[_stageId];
    }

    // Returns the current Stage in progress for a mint. If a Project is finished, it will return an empty Stage
    function currentStage(uint256 _projectId) public view returns (Stage memory) {
        Stage[] memory _stages = allStages(_projectId); // 0 indexed
        uint length = _stages.length;

        for(uint i; i < length;) {
            if (block.timestamp >= _stages[i].stageStartTime) {
                // If there is no subsequent stage, return the current one
                if (i + 1 >= length) {
                    return _stages[i];
                }

                // If the next stage's start time is in the future, return the current one
                if (block.timestamp < _stages[i + 1].stageStartTime) {
                    return _stages[i];
                }
            }

            unchecked { ++i; }
        }

        Stage memory empty;
        return empty;
    }

    // Returns the current Stage's id
    function currentStageId(uint256 _projectId) public view returns (uint8) {
        return currentStage(_projectId).id;
    }

    // Returns the current Stage price in wei
    function currentStagePrice(uint256  _projectId) public view returns (uint256) {
        return currentStage(_projectId).pricePerTokenInWei;
    }

    // Returns the next Stage as a struct
    function nextStage(uint256  _projectId) public view returns (Stage memory) {
        ProjectConfig storage config = projectConfig[_projectId];
        Stage memory _currentStage = currentStage(_projectId);

        if (_currentStage.id == 0) {
            return _currentStage;
        } else if (config.stages[config.currentStageId + 1].id != 0) {
            return config.stages[config.currentStageId + 1];
        } else {
            Stage memory empty;
            return empty;
        }
    }

    // Returns the next Stage's price in wei
    function nextStagePrice(uint256 _projectId) external view returns (uint256) {
        return nextStage(_projectId).pricePerTokenInWei;
    }

    // Returns the next stage start time as a Unix timestamp in seconds
    function startingTimeOfNextStage(uint256 _projectId) external view returns (uint256) {
        return nextStage(_projectId).stageStartTime;
    }

    /**
     * @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 = currentStagePrice(_projectId);
        currencySymbol = "ETH";
        currencyAddress = address(0);
    }

    function updateSystemAddress(uint256 _projectId, address _address) external onlyArtist(_projectId) {
        ProjectConfig storage _projectConfig = projectConfig[_projectId];
        _projectConfig.systemAddress = _address;
    }

    /// @notice Checks if the private key that singed the nonce matches the system address of the contract
    function isValidSignature(
        address _systemAddress,
        bytes32 hash,
        bytes memory signature
    ) internal pure returns (bool) {
        // Artist Owner should call `updateSystemAddress` with
        // the address corresponding to the private key that
        // signs the data payload from the backend being fed into the mint function.
        require(_systemAddress != address(0), "Missing System Address");

        bytes32 signedHash = hash.toEthSignedMessageHash();
        return signedHash.recover(signature) == _systemAddress;
    }

    /*//////////////////////////////////////////////////////////////
                              UNSUPPORTED
    //////////////////////////////////////////////////////////////*/

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

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

    /**
    * @notice Inactive function
    */
    function purchase(uint256, bytes32[] calldata) external payable returns (uint256) {
        revert ActionNotSupported();
    }

    /**
    * @notice Inactive function
    */
    function purchaseTo(address, uint256, bytes32[] calldata) external payable returns (uint256) {
        revert ActionNotSupported();
    }

    /**
    * @notice Inactive function
    */
    function togglePurchaseToDisabled(uint256) external pure {
        revert ActionNotSupported();
    }

}

File 2 of 21 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (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() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

    function _nonReentrantAfter() private {
        // 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 21 : 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 21 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 5 of 21 : MerkleProof.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * 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.
 * OpenZeppelin's JavaScript library generates merkle trees that are safe
 * against this attack out of the box.
 */
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 simultaneously proven to be a part of a merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _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}
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _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 sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * _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}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _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 6 of 21 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "./math/Math.sol";

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

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

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

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

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

File 8 of 21 : 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 9 of 21 : 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 10 of 21 : 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 11 of 21 : 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 12 of 21 : 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 13 of 21 : 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 14 of 21 : 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 15 of 21 : 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 16 of 21 : 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 17 of 21 : 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 18 of 21 : 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 19 of 21 : 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 20 of 21 : 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 21 of 21 : MinterBase_v0_1_1.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "./interfaces/IMinterBaseV0.sol";
import "./interfaces/IGenArt721CoreContractV3_Base.sol";
import "./interfaces/IGenArt721CoreContractV3.sol";
import "./interfaces/IGenArt721CoreContractV3_Engine.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": false,
    "runs": 200
  },
  "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"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ActionNotSupported","type":"error"},{"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":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"allStages","outputs":[{"components":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint8","name":"transactionMaxInvocations","type":"uint8"},{"internalType":"uint32","name":"stageStartTime","type":"uint32"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"pricePerTokenInWei","type":"uint256"}],"internalType":"struct MultiMerkleMinterV1.Stage[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint32","name":"_stageStartTime","type":"uint32"},{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"_pricePerTokenInWei","type":"uint256"},{"internalType":"uint8","name":"_transactionMaxInvocations","type":"uint8"}],"name":"createNewStage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"currentStage","outputs":[{"components":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint8","name":"transactionMaxInvocations","type":"uint8"},{"internalType":"uint32","name":"stageStartTime","type":"uint32"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"pricePerTokenInWei","type":"uint256"}],"internalType":"struct MultiMerkleMinterV1.Stage","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"currentStageId","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"currentStagePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"genArt721CoreAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint8","name":"_stageId","type":"uint8"},{"internalType":"address","name":"_address","type":"address"}],"name":"getAddressMintedCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"uint256","name":"_productId","type":"uint256"},{"internalType":"uint8","name":"_stageId","type":"uint8"}],"name":"getStage","outputs":[{"components":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint8","name":"transactionMaxInvocations","type":"uint8"},{"internalType":"uint32","name":"stageStartTime","type":"uint32"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"pricePerTokenInWei","type":"uint256"}],"internalType":"struct MultiMerkleMinterV1.Stage","name":"","type":"tuple"}],"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":"uint256","name":"_projectId","type":"uint256"}],"name":"nextStage","outputs":[{"components":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint8","name":"transactionMaxInvocations","type":"uint8"},{"internalType":"uint32","name":"stageStartTime","type":"uint32"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"pricePerTokenInWei","type":"uint256"}],"internalType":"struct MultiMerkleMinterV1.Stage","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"nextStagePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"uint8","name":"currentStageId","type":"uint8"},{"internalType":"uint8","name":"stagesCount","type":"uint8"},{"internalType":"uint24","name":"maxInvocations","type":"uint24"},{"internalType":"address","name":"systemAddress","type":"address"}],"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":"","type":"uint256"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"purchase","outputs":[{"internalType":"uint256","name":"","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":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"bytes","name":"_data","type":"bytes"},{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"},{"internalType":"string","name":"_nonce","type":"string"}],"name":"purchaseMMM","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"purchaseTo","outputs":[{"internalType":"uint256","name":"","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":"uint256","name":"_projectId","type":"uint256"}],"name":"setProjectMaxInvocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"startingTimeOfNextStage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address[]","name":"_addresses","type":"address[]"},{"internalType":"uint256[]","name":"_counts","type":"uint256[]"}],"name":"teamMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"togglePurchaseToDisabled","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint8","name":"_stageId","type":"uint8"},{"internalType":"bytes32","name":"_root","type":"bytes32"}],"name":"updateMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint8","name":"_stageId","type":"uint8"},{"internalType":"uint256","name":"_pricePerTokenInWei","type":"uint256"}],"name":"updatePricePerTokenInWei","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint8","name":"_stageId","type":"uint8"},{"internalType":"uint32","name":"_newStartingTime","type":"uint32"}],"name":"updateStartingTimeForStage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address","name":"_address","type":"address"}],"name":"updateSystemAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"},{"internalType":"address","name":"_address","type":"address"}],"name":"verifyAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}]

6101206040523480156200001257600080fd5b50604051620061d3380380620061d383398181016040528101906200003891906200044d565b81600160008190555062000052816200021e60201b60201c565b1515608081151581525050508173ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff16815250508173ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff16815250508073ffffffffffffffffffffffffffffffffffffffff1660e08173ffffffffffffffffffffffffffffffffffffffff16815250508073ffffffffffffffffffffffffffffffffffffffff166101008173ffffffffffffffffffffffffffffffffffffffff16815250508173ffffffffffffffffffffffffffffffffffffffff166101005173ffffffffffffffffffffffffffffffffffffffff166392a10f836040518163ffffffff1660e01b81526004016020604051808303816000875af115801562000197573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001bd919062000494565b73ffffffffffffffffffffffffffffffffffffffff161462000216576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200020d9062000527565b60405180910390fd5b50506200076c565b60008060008060405160240162000237929190620005a3565b6040516020818303038152906040527f8639415b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000808473ffffffffffffffffffffffffffffffffffffffff1683604051620002e0919062000649565b6000604051808303816000865af19150503d80600081146200031f576040519150601f19603f3d011682016040523d82523d6000602084013e62000324565b606091505b5091509150816200036c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200036390620006d8565b60405180910390fd5b60008151905060c0810362000389576000945050505050620003de565b6101008103620003a1576001945050505050620003de565b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620003d5906200074a565b60405180910390fd5b919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200041582620003e8565b9050919050565b620004278162000408565b81146200043357600080fd5b50565b60008151905062000447816200041c565b92915050565b60008060408385031215620004675762000466620003e3565b5b6000620004778582860162000436565b92505060206200048a8582860162000436565b9150509250929050565b600060208284031215620004ad57620004ac620003e3565b5b6000620004bd8482850162000436565b91505092915050565b600082825260208201905092915050565b7f496c6c6567616c20436f6e74726163742050616972696e670000000000000000600082015250565b60006200050f601883620004c6565b91506200051c82620004d7565b602082019050919050565b60006020820190508181036000830152620005428162000500565b9050919050565b6000819050919050565b600060ff82169050919050565b6000819050919050565b60006200058b620005856200057f8462000549565b62000560565b62000553565b9050919050565b6200059d816200056a565b82525050565b6000604082019050620005ba600083018562000592565b620005c9602083018462000592565b9392505050565b600081519050919050565b600081905092915050565b60005b8381101562000606578082015181840152602081019050620005e9565b60008484015250505050565b60006200061f82620005d0565b6200062b8185620005db565b93506200063d818560208601620005e6565b80840191505092915050565b600062000657828462000612565b915081905092915050565b7f6765745072696d617279526576656e756553706c69747328292063616c6c206660008201527f61696c6564000000000000000000000000000000000000000000000000000000602082015250565b6000620006c0602583620004c6565b9150620006cd8262000662565b604082019050919050565b60006020820190508181036000830152620006f381620006b1565b9050919050565b7f556e657870656374656420726576656e75652073706c69742062797465730000600082015250565b600062000732601e83620004c6565b91506200073f82620006fa565b602082019050919050565b60006020820190508181036000830152620007658162000723565b9050919050565b60805160a05160c05160e051610100516159ce6200080560003960008181611bc901526128010152600061261301526000818161090201528181610a6a01528181610bd001528181610cda01528181610f0b015281816110e301528181611fa4015281816126a5015281816129080152612a12015260008181611cef0152611f690152600081816125ef0152612f9301526159ce6000f3fe6080604052600436106101f95760003560e01c8063891407c01161010d578063c8a1f9b1116100a0578063e9d1e8ac1161006f578063e9d1e8ac146107e5578063efef39a114610810578063f002d9f314610840578063f7bd4b8814610869578063f849584d14610892576101f9565b8063c8a1f9b114610722578063da7e7c501461075f578063db6921b21461078f578063dd85582f146107ba576101f9565b8063a0b4cd18116100dc578063a0b4cd181461063d578063b836fefe1461067a578063c21ab44e146106a3578063c71b1b71146106e0576101f9565b8063891407c014610568578063894edcd414610598578063903488df146105d557806392a10f8314610612576101f9565b806340d1397e1161019057806356690aaf1161015f57806356690aaf1461045557806358f2a2b3146104925780635a23ac25146104cf578063676d9fcd1461050c578063774159c614610528576101f9565b806340d1397e14610389578063462add46146103b25780634fbd7fa1146103ef57806352b9c2d614610418576101f9565b80631ec2e523116101cc5780631ec2e523146102b6578063202c5805146102f357806325075249146103235780633aa5fe591461034c576101f9565b8063087b0581146101fe5780630f113ede1461023b5780631593e74c146102645780631607c9951461028d575b600080fd5b34801561020a57600080fd5b50610225600480360381019061022091906137c9565b6108cf565b6040516102329190613858565b60405180910390f35b34801561024757600080fd5b50610262600480360381019061025d91906138a9565b6108ff565b005b34801561027057600080fd5b5061028b60048036038101906102869190613922565b610a67565b005b34801561029957600080fd5b506102b460048036038101906102af9190613975565b610bcd565b005b3480156102c257600080fd5b506102dd60048036038101906102d891906139b5565b610eac565b6040516102ea9190613a24565b60405180910390f35b61030d60048036038101906103089190613a3f565b610ed4565b60405161031a9190613ac2565b60405180910390f35b34801561032f57600080fd5b5061034a60048036038101906103459190613add565b610f08565b005b34801561035857600080fd5b50610373600480360381019061036e9190613b30565b611051565b6040516103809190613a24565b60405180910390f35b34801561039557600080fd5b506103b060048036038101906103ab9190613b5d565b611081565b005b3480156103be57600080fd5b506103d960048036038101906103d49190613b5d565b6110b3565b6040516103e69190613858565b60405180910390f35b3480156103fb57600080fd5b5061041660048036038101906104119190613bc6565b6110e0565b005b34801561042457600080fd5b5061043f600480360381019061043a9190613c41565b611404565b60405161044c9190613ac2565b60405180910390f35b34801561046157600080fd5b5061047c60048036038101906104779190613b5d565b61147a565b6040516104899190613ac2565b60405180910390f35b34801561049e57600080fd5b506104b960048036038101906104b49190613b5d565b6114ae565b6040516104c69190613ac2565b60405180910390f35b3480156104db57600080fd5b506104f660048036038101906104f19190613b5d565b6114c4565b6040516105039190613d38565b60405180910390f35b61052660048036038101906105219190613dff565b6115cb565b005b34801561053457600080fd5b5061054f600480360381019061054a9190613b5d565b611d31565b60405161055f9493929190613fae565b60405180910390f35b610582600480360381019061057d9190613ffa565b611db1565b60405161058f9190613ac2565b60405180910390f35b3480156105a457600080fd5b506105bf60048036038101906105ba9190613b5d565b611de5565b6040516105cc9190613d38565b60405180910390f35b3480156105e157600080fd5b506105fc60048036038101906105f79190613b5d565b611f51565b6040516106099190613ac2565b60405180910390f35b34801561061e57600080fd5b50610627611f67565b604051610634919061403a565b60405180910390f35b34801561064957600080fd5b50610664600480360381019061065f9190613b5d565b611f8b565b6040516106719190614064565b60405180910390f35b34801561068657600080fd5b506106a1600480360381019061069c919061407f565b611fa1565b005b3480156106af57600080fd5b506106ca60048036038101906106c59190613b5d565b612415565b6040516106d79190613ac2565b60405180910390f35b3480156106ec57600080fd5b5061070760048036038101906107029190613b5d565b612431565b604051610719969594939291906140f0565b60405180910390f35b34801561072e57600080fd5b5061074960048036038101906107449190613b5d565b6124d0565b6040516107569190614268565b60405180910390f35b6107796004803603810190610774919061428a565b6125b9565b6040516107869190613ac2565b60405180910390f35b34801561079b57600080fd5b506107a46125ed565b6040516107b19190613858565b60405180910390f35b3480156107c657600080fd5b506107cf612611565b6040516107dc919061403a565b60405180910390f35b3480156107f157600080fd5b506107fa612635565b60405161080791906142ea565b60405180910390f35b61082a60048036038101906108259190613b5d565b61266e565b6040516108379190613ac2565b60405180910390f35b34801561084c57600080fd5b50610867600480360381019061086291906143b8565b6126a2565b005b34801561087557600080fd5b50610890600480360381019061088b9190613b5d565b612905565b005b34801561089e57600080fd5b506108b960048036038101906108b4919061444d565b612b5d565b6040516108c69190613d38565b60405180910390f35b60006108f5856108de84611051565b8686612c199190939291909392919063ffffffff16565b9050949350505050565b817f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b81526004016109599190613ac2565b602060405180830381865afa158015610976573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099a91906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a07576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109fe90614544565b60405180910390fd5b6000600160008581526020019081526020016000209050828160000160076101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050565b827f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b8152600401610ac19190613ac2565b602060405180830381865afa158015610ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0291906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610b6f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b6690614544565b60405180910390fd5b600060016000868152602001908152602001600020905060018160000160016101000a81548160ff021916908315150217905550828160030160008660ff1660ff168152602001908152602001600020600201819055505050505050565b817f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b8152600401610c279190613ac2565b602060405180830381865afa158015610c44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c6891906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610cd5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ccc90614544565b60405180910390fd5b6000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630ea5613f866040518263ffffffff1660e01b8152600401610d319190613ac2565b60c060405180830381865afa158015610d4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7291906145a5565b90919250909150905050809350819250505081841115610dc7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dbe906146ca565b60405180910390fd5b80841015610e0a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e0190614782565b60405180910390fd5b836001600087815260200190815260200160002060000160046101000a81548162ffffff021916908362ffffff1602179055508381146001600087815260200190815260200160002060000160006101000a81548160ff021916908315150217905550847f8445d32a2ee05956c6c842357ca16ee41e92657b1cbcbf1c94f500672e48c3b185604051610e9d9190613ac2565b60405180910390a25050505050565b6000610ecb610eba83611051565b8585612c329290919263ffffffff16565b90509392505050565b60006040517fd0df97cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b827f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b8152600401610f629190613ac2565b602060405180830381865afa158015610f7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa391906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611010576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161100790614544565b60405180910390fd5b6000600160008681526020019081526020016000209050828160030160008660ff1660ff168152602001908152602001600020600101819055505050505050565b60008160405160200161106491906147ea565b604051602081830303815290604052805190602001209050919050565b6040517fd0df97cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006001600083815260200190815260200160002060000160009054906101000a900460ff169050919050565b847f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b815260040161113a9190613ac2565b602060405180830381865afa158015611157573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117b91906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146111e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111df90614544565b60405180910390fd5b428563ffffffff1611611230576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161122790614851565b60405180910390fd5b60006001600088815260200190815260200160002090508060000160009054906101000a900460ff1615611299576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611290906148bd565b60405180910390fd5b6000808260000160039054906101000a900460ff1660ff16036112dc576001905060018260000160016101000a81548160ff0219169083151502179055506112fd565b60018260000160039054906101000a900460ff166112fa919061490c565b90505b81600001600381819054906101000a900460ff1660010191906101000a81548160ff021916908360ff1602179055506040518060a001604052808260ff1681526020018560ff1681526020018863ffffffff168152602001878152602001868152508260030160008360ff1660ff16815260200190815260200160002060008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a81548160ff021916908360ff16021790555060408201518160000160026101000a81548163ffffffff021916908363ffffffff16021790555060608201518160010155608082015181600201559050505050505050505050565b60006001600085815260200190815260200160002060020160008460ff1660ff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509392505050565b60006001600083815260200190815260200160002060000160049054906101000a900462ffffff1662ffffff169050919050565b60006114b982611de5565b608001519050919050565b6114cc613688565b60006114d7836124d0565b905060008151905060005b818110156115b6578281815181106114fd576114fc614941565b5b60200260200101516040015163ffffffff1642106115ab57816001826115239190614970565b1061154d5782818151811061153b5761153a614941565b5b602002602001015193505050506115c6565b8260018261155b9190614970565b8151811061156c5761156b614941565b5b60200260200101516040015163ffffffff164210156115aa5782818151811061159857611597614941565b5b602002602001015193505050506115c6565b5b8060010190506114e2565b506115bf613688565b8093505050505b919050565b6115d3612c8a565b60008a905060008a905060008a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050905060006001600085815260200190815260200160002090508060000160009054906101000a900460ff161561168f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611686906149f0565b60405180910390fd5b600061169a856114c4565b9050806000015160ff168260000160029054906101000a900460ff1660ff16146116e05780600001518260000160026101000a81548160ff021916908360ff1602179055505b6000816040015163ffffffff160361172d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161172490614a5c565b60405180910390fd5b8160000160019054906101000a900460ff1661177e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161177590614ac8565b60405180910390fd5b60008461178a87611f51565b6117949190614ae8565b90508034146117d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117cf90614b76565b60405180910390fd5b8260010188886040516117ec929190614bd5565b908152602001604051809103902060009054906101000a900460ff1615611848576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183f90614c3a565b60405180910390fd5b6118a38360000160079054906101000a900473ffffffffffffffffffffffffffffffffffffffff1633878b8b6040516020016118879493929190614c7b565b6040516020818303038152906040528051906020012086612cd9565b6118e2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118d990614d02565b60405180910390fd5b6000808d8d8101906118f49190614d37565b915091503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614611966576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161195d90614dc3565b60405180910390fd5b60008082119050600073ffffffffffffffffffffffffffffffffffffffff1661198e8a6114c4565b6060015160001c73ffffffffffffffffffffffffffffffffffffffff1614611a6c576000336040516020016119c391906147ea565b604051602081830303815290604052805190602001209050611a2b8e8e80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050876060015183612da1565b611a6a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a6190614e2f565b60405180910390fd5b505b8015611b1d578188876002016000886000015160ff1660ff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054611adb9190614970565b1115611b1c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b1390614e9b565b60405180910390fd5b5b6001866001018c8c604051611b33929190614bd5565b908152602001604051809103902060006101000a81548160ff02191690831515021790555087866002016000876000015160ff1660ff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060005b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630d4d1513338d336040518463ffffffff1660e01b8152600401611c2493929190614ebb565b6020604051808303816000875af1158015611c43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c679190614ef2565b905060018860000160049054906101000a900462ffffff160362ffffff16620f42408281611c9857611c97614f1f565b5b0603611cda5760018860000160006101000a81548160ff02191690831515021790555060008860000160026101000a81548160ff021916908360ff1602179055505b81600101915050888110611bc557611d138a347f0000000000000000000000000000000000000000000000000000000000000000612db8565b50505050505050505050611d25612e9b565b50505050505050505050565b60008060606000806001600087815260200190815260200160002090508060000160019054906101000a900460ff169450611d6b86611f51565b93506040518060400160405280600381526020017f4554480000000000000000000000000000000000000000000000000000000000815250925060009150509193509193565b60006040517fd0df97cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ded613688565b60006001600084815260200190815260200160002090506000611e0f846114c4565b90506000816000015160ff1603611e2a578092505050611f4c565b600082600301600060018560000160029054906101000a900460ff16611e50919061490c565b60ff1660ff16815260200190815260200160002060000160009054906101000a900460ff1660ff1614611f3d5781600301600060018460000160029054906101000a900460ff16611ea1919061490c565b60ff1660ff1681526020019081526020016000206040518060a00160405290816000820160009054906101000a900460ff1660ff1660ff1681526020016000820160019054906101000a900460ff1660ff1660ff1681526020016000820160029054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820154815260200160028201548152505092505050611f4c565b611f45613688565b8093505050505b919050565b6000611f5c826114c4565b608001519050919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000611f96826114c4565b600001519050919050565b827f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b8152600401611ffb9190613ac2565b602060405180830381865afa158015612018573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061203c91906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146120a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120a090614544565b60405180910390fd5b60008360ff16116120ef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120e690614f9a565b60405180910390fd5b600060016000868152602001908152602001600020905060008160000160039054906101000a900460ff1660ff161161215d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161215490615006565b60405180910390fd5b60008363ffffffff16116121a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161219d90614f9a565b60405180910390fd5b428363ffffffff16116121ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121e590615072565b60405180910390fd5b8060000160039054906101000a900460ff1660ff168460ff161115612248576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161223f906150de565b60405180910390fd5b8060000160039054906101000a900460ff1660ff168460ff16036122a857828160030160008660ff1660ff16815260200190815260200160002060000160026101000a81548163ffffffff021916908363ffffffff16021790555061240e565b8060000160039054906101000a900460ff1660ff166001856122ca919061490c565b60ff161161240d5760008160030160006001876122e7919061490c565b60ff1660ff1681526020019081526020016000206040518060a00160405290816000820160009054906101000a900460ff1660ff1660ff1681526020016000820160019054906101000a900460ff1660ff1660ff1681526020016000820160029054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600182015481526020016002820154815250509050806040015163ffffffff168463ffffffff16106123ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016123c59061514a565b60405180910390fd5b838260030160008760ff1660ff16815260200190815260200160002060000160026101000a81548163ffffffff021916908363ffffffff160217905550505b5b5050505050565b600061242082611de5565b6040015163ffffffff169050919050565b60016020528060005260406000206000915090508060000160009054906101000a900460ff16908060000160019054906101000a900460ff16908060000160029054906101000a900460ff16908060000160039054906101000a900460ff16908060000160049054906101000a900462ffffff16908060000160079054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905086565b6060600060016000848152602001908152602001600020905060008160000160039054906101000a900460ff16905060008160ff1667ffffffffffffffff81111561251e5761251d61516a565b5b60405190808252806020026020018201604052801561255757816020015b612544613688565b81526020019060019003908161253c5790505b50905060005b8260ff168160ff1610156125ad576125818660018361257c919061490c565b612b5d565b828260ff168151811061259757612596614941565b5b602002602001018190525080600101905061255d565b50809350505050919050565b60006040517fd0df97cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6040518060400160405280601381526020017f4d756c74694d65726b6c654d696e74657256310000000000000000000000000081525081565b60006040517fd0df97cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b847f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b81526004016126fc9190613ac2565b602060405180830381865afa158015612719573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273d91906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146127aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127a190614544565b60405180910390fd5b8282905085859050146127f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127e9906151e5565b60405180910390fd5b60008585905090506000805b5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630d4d151389898581811061284e5761284d614941565b5b90506020020160208101906128639190613b30565b8b336040518463ffffffff1660e01b815260040161288393929190614ebb565b6020604051808303816000875af11580156128a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128c69190614ef2565b508060010190508585838181106128e0576128df614941565b5b9050602002013581106127ff578160010191508282106127fe57505050505050505050565b807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b815260040161295f9190613ac2565b602060405180830381865afa15801561297c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129a091906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612a0d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a0490614544565b60405180910390fd5b6000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630ea5613f856040518263ffffffff1660e01b8152600401612a699190613ac2565b60c060405180830381865afa158015612a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aaa91906145a5565b909192509091509050508093508192505050816001600086815260200190815260200160002060000160046101000a81548162ffffff021916908362ffffff1602179055508181146001600086815260200190815260200160002060000160006101000a81548160ff021916908315150217905550837f8445d32a2ee05956c6c842357ca16ee41e92657b1cbcbf1c94f500672e48c3b183604051612b4f9190613ac2565b60405180910390a250505050565b612b65613688565b6001600084815260200190815260200160002060030160008360ff1660ff1681526020019081526020016000206040518060a00160405290816000820160009054906101000a900460ff1660ff1660ff1681526020016000820160019054906101000a900460ff1660ff1660ff1681526020016000820160029054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160018201548152602001600282015481525050905092915050565b600082612c27868685612c32565b149050949350505050565b60008082905060005b85859050811015612c7e57612c6982878784818110612c5d57612c5c614941565b5b90506020020135612ea5565b91508080612c7690615205565b915050612c3b565b50809150509392505050565b600260005403612ccf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cc690615299565b60405180910390fd5b6002600081905550565b60008073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603612d49576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d4090615305565b60405180910390fd5b6000612d5484612ed0565b90508473ffffffffffffffffffffffffffffffffffffffff16612d808483612f0090919063ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff16149150509392505050565b600082612dae8584612f27565b1490509392505050565b6000341115612e96576000808334612dd09190615325565b90506000811115612e88573373ffffffffffffffffffffffffffffffffffffffff1681604051612dff9061538a565b60006040518083038185875af1925050503d8060008114612e3c576040519150601f19603f3d011682016040523d82523d6000602084013e612e41565b606091505b50508092505081612e87576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e7e906153eb565b60405180910390fd5b5b612e93858585612f7d565b50505b505050565b6001600081905550565b6000818310612ebd57612eb882846133d8565b612ec8565b612ec783836133d8565b5b905092915050565b600081604051602001612ee39190615478565b604051602081830303815290604052805190602001209050919050565b6000806000612f0f85856133ef565b91509150612f1c81613440565b819250505092915050565b60008082905060005b8451811015612f7257612f5d82868381518110612f5057612f4f614941565b5b6020026020010151612ea5565b91508080612f6a90615205565b915050612f30565b508091505092915050565b60008211156133d35760008060008060008060007f000000000000000000000000000000000000000000000000000000000000000015613114576000808973ffffffffffffffffffffffffffffffffffffffff16638639415b8d8d6040518363ffffffff1660e01b8152600401612ff592919061549e565b61010060405180830381865afa158015613013573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061303791906154c7565b809a50819b50829c50839d50849850859950869e50879f505050505050505050600082111561310d578073ffffffffffffffffffffffffffffffffffffffff16826040516130849061538a565b60006040518083038185875af1925050503d80600081146130c1576040519150601f19603f3d011682016040523d82523d6000602084013e6130c6565b606091505b5050809950508861310c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613103906155c9565b60405180910390fd5b5b50506131a9565b8773ffffffffffffffffffffffffffffffffffffffff16638639415b8b8b6040518363ffffffff1660e01b815260040161314f92919061549e565b60c060405180830381865afa15801561316c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061319091906155e9565b809650819750829850839950849a50859b505050505050505b600086111561325f578473ffffffffffffffffffffffffffffffffffffffff16866040516131d69061538a565b60006040518083038185875af1925050503d8060008114613213576040519150601f19603f3d011682016040523d82523d6000602084013e613218565b606091505b5050809750508661325e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613255906156c2565b60405180910390fd5b5b6000841115613315578273ffffffffffffffffffffffffffffffffffffffff168460405161328c9061538a565b60006040518083038185875af1925050503d80600081146132c9576040519150601f19603f3d011682016040523d82523d6000602084013e6132ce565b606091505b50508097505086613314576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161330b9061572e565b60405180910390fd5b5b60008211156133cb578073ffffffffffffffffffffffffffffffffffffffff16826040516133429061538a565b60006040518083038185875af1925050503d806000811461337f576040519150601f19603f3d011682016040523d82523d6000602084013e613384565b606091505b505080975050866133ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016133c19061579a565b60405180910390fd5b5b505050505050505b505050565b600082600052816020526040600020905092915050565b60008060418351036134305760008060006020860151925060408601519150606086015160001a9050613424878285856135a6565b94509450505050613439565b60006002915091505b9250929050565b60006004811115613454576134536157ba565b5b816004811115613467576134666157ba565b5b03156135a35760016004811115613481576134806157ba565b5b816004811115613494576134936157ba565b5b036134d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016134cb90615835565b60405180910390fd5b600260048111156134e8576134e76157ba565b5b8160048111156134fb576134fa6157ba565b5b0361353b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613532906158a1565b60405180910390fd5b6003600481111561354f5761354e6157ba565b5b816004811115613562576135616157ba565b5b036135a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161359990615933565b60405180910390fd5b5b50565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08360001c11156135e157600060039150915061367f565b6000600187878787604051600081526020016040526040516136069493929190615953565b6020604051602081039080840390855afa158015613628573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036136765760006001925092505061367f565b80600092509250505b94509492505050565b6040518060a00160405280600060ff168152602001600060ff168152602001600063ffffffff16815260200160008019168152602001600081525090565b600080fd5b600080fd5b6000819050919050565b6136e3816136d0565b81146136ee57600080fd5b50565b600081359050613700816136da565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261372b5761372a613706565b5b8235905067ffffffffffffffff8111156137485761374761370b565b5b60208301915083602082028301111561376457613763613710565b5b9250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006137968261376b565b9050919050565b6137a68161378b565b81146137b157600080fd5b50565b6000813590506137c38161379d565b92915050565b600080600080606085870312156137e3576137e26136c6565b5b60006137f1878288016136f1565b945050602085013567ffffffffffffffff811115613812576138116136cb565b5b61381e87828801613715565b93509350506040613831878288016137b4565b91505092959194509250565b60008115159050919050565b6138528161383d565b82525050565b600060208201905061386d6000830184613849565b92915050565b6000819050919050565b61388681613873565b811461389157600080fd5b50565b6000813590506138a38161387d565b92915050565b600080604083850312156138c0576138bf6136c6565b5b60006138ce85828601613894565b92505060206138df858286016137b4565b9150509250929050565b600060ff82169050919050565b6138ff816138e9565b811461390a57600080fd5b50565b60008135905061391c816138f6565b92915050565b60008060006060848603121561393b5761393a6136c6565b5b600061394986828701613894565b935050602061395a8682870161390d565b925050604061396b86828701613894565b9150509250925092565b6000806040838503121561398c5761398b6136c6565b5b600061399a85828601613894565b92505060206139ab85828601613894565b9150509250929050565b6000806000604084860312156139ce576139cd6136c6565b5b600084013567ffffffffffffffff8111156139ec576139eb6136cb565b5b6139f886828701613715565b93509350506020613a0b868287016137b4565b9150509250925092565b613a1e816136d0565b82525050565b6000602082019050613a396000830184613a15565b92915050565b60008060008060608587031215613a5957613a586136c6565b5b6000613a67878288016137b4565b9450506020613a7887828801613894565b935050604085013567ffffffffffffffff811115613a9957613a986136cb565b5b613aa587828801613715565b925092505092959194509250565b613abc81613873565b82525050565b6000602082019050613ad76000830184613ab3565b92915050565b600080600060608486031215613af657613af56136c6565b5b6000613b0486828701613894565b9350506020613b158682870161390d565b9250506040613b26868287016136f1565b9150509250925092565b600060208284031215613b4657613b456136c6565b5b6000613b54848285016137b4565b91505092915050565b600060208284031215613b7357613b726136c6565b5b6000613b8184828501613894565b91505092915050565b600063ffffffff82169050919050565b613ba381613b8a565b8114613bae57600080fd5b50565b600081359050613bc081613b9a565b92915050565b600080600080600060a08688031215613be257613be16136c6565b5b6000613bf088828901613894565b9550506020613c0188828901613bb1565b9450506040613c12888289016136f1565b9350506060613c2388828901613894565b9250506080613c348882890161390d565b9150509295509295909350565b600080600060608486031215613c5a57613c596136c6565b5b6000613c6886828701613894565b9350506020613c798682870161390d565b9250506040613c8a868287016137b4565b9150509250925092565b613c9d816138e9565b82525050565b613cac81613b8a565b82525050565b613cbb816136d0565b82525050565b613cca81613873565b82525050565b60a082016000820151613ce66000850182613c94565b506020820151613cf96020850182613c94565b506040820151613d0c6040850182613ca3565b506060820151613d1f6060850182613cb2565b506080820151613d326080850182613cc1565b50505050565b600060a082019050613d4d6000830184613cd0565b92915050565b60008083601f840112613d6957613d68613706565b5b8235905067ffffffffffffffff811115613d8657613d8561370b565b5b602083019150836001820283011115613da257613da1613710565b5b9250929050565b60008083601f840112613dbf57613dbe613706565b5b8235905067ffffffffffffffff811115613ddc57613ddb61370b565b5b602083019150836001820283011115613df857613df7613710565b5b9250929050565b60008060008060008060008060008060c08b8d031215613e2257613e216136c6565b5b6000613e308d828e01613894565b9a50506020613e418d828e01613894565b99505060408b013567ffffffffffffffff811115613e6257613e616136cb565b5b613e6e8d828e01613d53565b985098505060608b013567ffffffffffffffff811115613e9157613e906136cb565b5b613e9d8d828e01613d53565b965096505060808b013567ffffffffffffffff811115613ec057613ebf6136cb565b5b613ecc8d828e01613715565b945094505060a08b013567ffffffffffffffff811115613eef57613eee6136cb565b5b613efb8d828e01613da9565b92509250509295989b9194979a5092959850565b600081519050919050565b600082825260208201905092915050565b60005b83811015613f49578082015181840152602081019050613f2e565b60008484015250505050565b6000601f19601f8301169050919050565b6000613f7182613f0f565b613f7b8185613f1a565b9350613f8b818560208601613f2b565b613f9481613f55565b840191505092915050565b613fa88161378b565b82525050565b6000608082019050613fc36000830187613849565b613fd06020830186613ab3565b8181036040830152613fe28185613f66565b9050613ff16060830184613f9f565b95945050505050565b60008060408385031215614011576140106136c6565b5b600061401f858286016137b4565b925050602061403085828601613894565b9150509250929050565b600060208201905061404f6000830184613f9f565b92915050565b61405e816138e9565b82525050565b60006020820190506140796000830184614055565b92915050565b600080600060608486031215614098576140976136c6565b5b60006140a686828701613894565b93505060206140b78682870161390d565b92505060406140c886828701613bb1565b9150509250925092565b600062ffffff82169050919050565b6140ea816140d2565b82525050565b600060c0820190506141056000830189613849565b6141126020830188613849565b61411f6040830187614055565b61412c6060830186614055565b61413960808301856140e1565b61414660a0830184613f9f565b979650505050505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60a0820160008201516141936000850182613c94565b5060208201516141a66020850182613c94565b5060408201516141b96040850182613ca3565b5060608201516141cc6060850182613cb2565b5060808201516141df6080850182613cc1565b50505050565b60006141f1838361417d565b60a08301905092915050565b6000602082019050919050565b600061421582614151565b61421f818561415c565b935061422a8361416d565b8060005b8381101561425b57815161424288826141e5565b975061424d836141fd565b92505060018101905061422e565b5085935050505092915050565b60006020820190508181036000830152614282818461420a565b905092915050565b6000806000604084860312156142a3576142a26136c6565b5b60006142b186828701613894565b935050602084013567ffffffffffffffff8111156142d2576142d16136cb565b5b6142de86828701613715565b92509250509250925092565b600060208201905081810360008301526143048184613f66565b905092915050565b60008083601f84011261432257614321613706565b5b8235905067ffffffffffffffff81111561433f5761433e61370b565b5b60208301915083602082028301111561435b5761435a613710565b5b9250929050565b60008083601f84011261437857614377613706565b5b8235905067ffffffffffffffff8111156143955761439461370b565b5b6020830191508360208202830111156143b1576143b0613710565b5b9250929050565b6000806000806000606086880312156143d4576143d36136c6565b5b60006143e288828901613894565b955050602086013567ffffffffffffffff811115614403576144026136cb565b5b61440f8882890161430c565b9450945050604086013567ffffffffffffffff811115614432576144316136cb565b5b61443e88828901614362565b92509250509295509295909350565b60008060408385031215614464576144636136c6565b5b600061447285828601613894565b92505060206144838582860161390d565b9150509250929050565b60006144988261376b565b9050919050565b6144a88161448d565b81146144b357600080fd5b50565b6000815190506144c58161449f565b92915050565b6000602082840312156144e1576144e06136c6565b5b60006144ef848285016144b6565b91505092915050565b7f4f6e6c7920417274697374000000000000000000000000000000000000000000600082015250565b600061452e600b83613f1a565b9150614539826144f8565b602082019050919050565b6000602082019050818103600083015261455d81614521565b9050919050565b6000815190506145738161387d565b92915050565b6145828161383d565b811461458d57600080fd5b50565b60008151905061459f81614579565b92915050565b60008060008060008060c087890312156145c2576145c16136c6565b5b60006145d089828a01614564565b96505060206145e189828a01614564565b95505060406145f289828a01614590565b945050606061460389828a01614590565b935050608061461489828a01614564565b92505060a061462589828a01614590565b9150509295509295509295565b7f43616e6e6f7420696e6372656173652070726f6a656374206d617820696e766f60008201527f636174696f6e732061626f766520636f726520636f6e7472616374207365742060208201527f70726f6a656374206d617820696e766f636174696f6e73000000000000000000604082015250565b60006146b4605783613f1a565b91506146bf82614632565b606082019050919050565b600060208201905081810360008301526146e3816146a7565b9050919050565b7f43616e6e6f74207365742070726f6a656374206d617820696e766f636174696f60008201527f6e7320746f206c657373207468616e2063757272656e7420696e766f6361746960208201527f6f6e730000000000000000000000000000000000000000000000000000000000604082015250565b600061476c604383613f1a565b9150614777826146ea565b606082019050919050565b6000602082019050818103600083015261479b8161475f565b9050919050565b60008160601b9050919050565b60006147ba826147a2565b9050919050565b60006147cc826147af565b9050919050565b6147e46147df8261378b565b6147c1565b82525050565b60006147f682846147d3565b60148201915081905092915050565b7f4e6f20537461727420426c6f636b20496e205061737400000000000000000000600082015250565b600061483b601683613f1a565b915061484682614805565b602082019050919050565b6000602082019050818103600083015261486a8161482e565b9050919050565b7f4d696e746564204f75742043616e7420437265617465204e6577205374616765600082015250565b60006148a7602083613f1a565b91506148b282614871565b602082019050919050565b600060208201905081810360008301526148d68161489a565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614917826138e9565b9150614922836138e9565b9250828201905060ff81111561493b5761493a6148dd565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600061497b82613873565b915061498683613873565b925082820190508082111561499e5761499d6148dd565b5b92915050565b7f4d617820496e766f636174696f6e732052656163686564000000000000000000600082015250565b60006149da601783613f1a565b91506149e5826149a4565b602082019050919050565b60006020820190508181036000830152614a09816149cd565b9050919050565b7f437265617465204120537461676520546f20426567696e000000000000000000600082015250565b6000614a46601783613f1a565b9150614a5182614a10565b602082019050919050565b60006020820190508181036000830152614a7581614a39565b9050919050565b7f5072696365204e6f7420436f6e66696775726564000000000000000000000000600082015250565b6000614ab2601483613f1a565b9150614abd82614a7c565b602082019050919050565b60006020820190508181036000830152614ae181614aa5565b9050919050565b6000614af382613873565b9150614afe83613873565b9250828202614b0c81613873565b91508282048414831517614b2357614b226148dd565b5b5092915050565b7f57726f6e67205072696365000000000000000000000000000000000000000000600082015250565b6000614b60600b83613f1a565b9150614b6b82614b2a565b602082019050919050565b60006020820190508181036000830152614b8f81614b53565b9050919050565b600081905092915050565b82818337600083830152505050565b6000614bbc8385614b96565b9350614bc9838584614ba1565b82840190509392505050565b6000614be2828486614bb0565b91508190509392505050565b7f4e6f6e6365205573656400000000000000000000000000000000000000000000600082015250565b6000614c24600a83613f1a565b9150614c2f82614bee565b602082019050919050565b60006020820190508181036000830152614c5381614c17565b9050919050565b6000819050919050565b614c75614c7082613873565b614c5a565b82525050565b6000614c8782876147d3565b601482019150614c978286614c64565b602082019150614ca8828486614bb0565b915081905095945050505050565b7f496e76616c6964205369676e6174757265000000000000000000000000000000600082015250565b6000614cec601183613f1a565b9150614cf782614cb6565b602082019050919050565b60006020820190508181036000830152614d1b81614cdf565b9050919050565b600081359050614d318161449f565b92915050565b60008060408385031215614d4e57614d4d6136c6565b5b6000614d5c85828601614d22565b9250506020614d6d85828601613894565b9150509250929050565b7f4261642044617461000000000000000000000000000000000000000000000000600082015250565b6000614dad600883613f1a565b9150614db882614d77565b602082019050919050565b60006020820190508181036000830152614ddc81614da0565b9050919050565b7f4e6f74204f6e20416c6c6f776c69737400000000000000000000000000000000600082015250565b6000614e19601083613f1a565b9150614e2482614de3565b602082019050919050565b60006020820190508181036000830152614e4881614e0c565b9050919050565b7f43616e2774204d696e742054686174204d616e79000000000000000000000000600082015250565b6000614e85601483613f1a565b9150614e9082614e4f565b602082019050919050565b60006020820190508181036000830152614eb481614e78565b9050919050565b6000606082019050614ed06000830186613f9f565b614edd6020830185613ab3565b614eea6040830184613f9f565b949350505050565b600060208284031215614f0857614f076136c6565b5b6000614f1684828501614564565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f43616e2774206265203000000000000000000000000000000000000000000000600082015250565b6000614f84600a83613f1a565b9150614f8f82614f4e565b602082019050919050565b60006020820190508181036000830152614fb381614f77565b9050919050565b7f4e6f2053746167657320657869737420666f722070726f6a6563740000000000600082015250565b6000614ff0601b83613f1a565b9150614ffb82614fba565b602082019050919050565b6000602082019050818103600083015261501f81614fe3565b9050919050565b7f4d75737420626520696e20746865206675747572650000000000000000000000600082015250565b600061505c601583613f1a565b915061506782615026565b602082019050919050565b6000602082019050818103600083015261508b8161504f565b9050919050565b7f537461676520646f65736e277420657869737400000000000000000000000000600082015250565b60006150c8601383613f1a565b91506150d382615092565b602082019050919050565b600060208201905081810360008301526150f7816150bb565b9050919050565b7f53746167652063616e2774207374617274206166746572206e657874206f6e65600082015250565b6000615134602083613f1a565b915061513f826150fe565b602082019050919050565b6000602082019050818103600083015261516381615127565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f556e657175616c20617272617973000000000000000000000000000000000000600082015250565b60006151cf600e83613f1a565b91506151da82615199565b602082019050919050565b600060208201905081810360008301526151fe816151c2565b9050919050565b600061521082613873565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615242576152416148dd565b5b600182019050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b6000615283601f83613f1a565b915061528e8261524d565b602082019050919050565b600060208201905081810360008301526152b281615276565b9050919050565b7f4d697373696e672053797374656d204164647265737300000000000000000000600082015250565b60006152ef601683613f1a565b91506152fa826152b9565b602082019050919050565b6000602082019050818103600083015261531e816152e2565b9050919050565b600061533082613873565b915061533b83613873565b9250828203905081811115615353576153526148dd565b5b92915050565b600081905092915050565b50565b6000615374600083615359565b915061537f82615364565b600082019050919050565b600061539582615367565b9150819050919050565b7f526566756e64206661696c656400000000000000000000000000000000000000600082015250565b60006153d5600d83613f1a565b91506153e08261539f565b602082019050919050565b60006020820190508181036000830152615404816153c8565b9050919050565b7f19457468657265756d205369676e6564204d6573736167653a0a333200000000600082015250565b6000615441601c83614b96565b915061544c8261540b565b601c82019050919050565b6000819050919050565b61547261546d826136d0565b615457565b82525050565b600061548382615434565b915061548f8284615461565b60208201915081905092915050565b60006040820190506154b36000830185613ab3565b6154c06020830184613ab3565b9392505050565b600080600080600080600080610100898b0312156154e8576154e76136c6565b5b60006154f68b828c01614564565b98505060206155078b828c016144b6565b97505060406155188b828c01614564565b96505060606155298b828c016144b6565b955050608061553a8b828c01614564565b94505060a061554b8b828c016144b6565b93505060c061555c8b828c01614564565b92505060e061556d8b828c016144b6565b9150509295985092959890939650565b7f506c6174666f726d2050726f7669646572207061796d656e74206661696c6564600082015250565b60006155b3602083613f1a565b91506155be8261557d565b602082019050919050565b600060208201905081810360008301526155e2816155a6565b9050919050565b60008060008060008060c08789031215615606576156056136c6565b5b600061561489828a01614564565b965050602061562589828a016144b6565b955050604061563689828a01614564565b945050606061564789828a016144b6565b935050608061565889828a01614564565b92505060a061566989828a016144b6565b9150509295509295509295565b7f52656e6465722050726f7669646572207061796d656e74206661696c65640000600082015250565b60006156ac601e83613f1a565b91506156b782615676565b602082019050919050565b600060208201905081810360008301526156db8161569f565b9050919050565b7f417274697374207061796d656e74206661696c65640000000000000000000000600082015250565b6000615718601583613f1a565b9150615723826156e2565b602082019050919050565b600060208201905081810360008301526157478161570b565b9050919050565b7f4164646974696f6e616c205061796565207061796d656e74206661696c656400600082015250565b6000615784601f83613f1a565b915061578f8261574e565b602082019050919050565b600060208201905081810360008301526157b381615777565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f45434453413a20696e76616c6964207369676e61747572650000000000000000600082015250565b600061581f601883613f1a565b915061582a826157e9565b602082019050919050565b6000602082019050818103600083015261584e81615812565b9050919050565b7f45434453413a20696e76616c6964207369676e6174757265206c656e67746800600082015250565b600061588b601f83613f1a565b915061589682615855565b602082019050919050565b600060208201905081810360008301526158ba8161587e565b9050919050565b7f45434453413a20696e76616c6964207369676e6174757265202773272076616c60008201527f7565000000000000000000000000000000000000000000000000000000000000602082015250565b600061591d602283613f1a565b9150615928826158c1565b604082019050919050565b6000602082019050818103600083015261594c81615910565b9050919050565b60006080820190506159686000830187613a15565b6159756020830186614055565b6159826040830185613a15565b61598f6060830184613a15565b9594505050505056fea2646970667358221220c51bb48a70916c9af3acc634454b189186eb302e1c2185c6726f2df957adb4e664736f6c63430008110033000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf650000000000000000000000003b90746ac1ccbfc5d6ac3a09d9930dc3b224a0f9

Deployed Bytecode

0x6080604052600436106101f95760003560e01c8063891407c01161010d578063c8a1f9b1116100a0578063e9d1e8ac1161006f578063e9d1e8ac146107e5578063efef39a114610810578063f002d9f314610840578063f7bd4b8814610869578063f849584d14610892576101f9565b8063c8a1f9b114610722578063da7e7c501461075f578063db6921b21461078f578063dd85582f146107ba576101f9565b8063a0b4cd18116100dc578063a0b4cd181461063d578063b836fefe1461067a578063c21ab44e146106a3578063c71b1b71146106e0576101f9565b8063891407c014610568578063894edcd414610598578063903488df146105d557806392a10f8314610612576101f9565b806340d1397e1161019057806356690aaf1161015f57806356690aaf1461045557806358f2a2b3146104925780635a23ac25146104cf578063676d9fcd1461050c578063774159c614610528576101f9565b806340d1397e14610389578063462add46146103b25780634fbd7fa1146103ef57806352b9c2d614610418576101f9565b80631ec2e523116101cc5780631ec2e523146102b6578063202c5805146102f357806325075249146103235780633aa5fe591461034c576101f9565b8063087b0581146101fe5780630f113ede1461023b5780631593e74c146102645780631607c9951461028d575b600080fd5b34801561020a57600080fd5b50610225600480360381019061022091906137c9565b6108cf565b6040516102329190613858565b60405180910390f35b34801561024757600080fd5b50610262600480360381019061025d91906138a9565b6108ff565b005b34801561027057600080fd5b5061028b60048036038101906102869190613922565b610a67565b005b34801561029957600080fd5b506102b460048036038101906102af9190613975565b610bcd565b005b3480156102c257600080fd5b506102dd60048036038101906102d891906139b5565b610eac565b6040516102ea9190613a24565b60405180910390f35b61030d60048036038101906103089190613a3f565b610ed4565b60405161031a9190613ac2565b60405180910390f35b34801561032f57600080fd5b5061034a60048036038101906103459190613add565b610f08565b005b34801561035857600080fd5b50610373600480360381019061036e9190613b30565b611051565b6040516103809190613a24565b60405180910390f35b34801561039557600080fd5b506103b060048036038101906103ab9190613b5d565b611081565b005b3480156103be57600080fd5b506103d960048036038101906103d49190613b5d565b6110b3565b6040516103e69190613858565b60405180910390f35b3480156103fb57600080fd5b5061041660048036038101906104119190613bc6565b6110e0565b005b34801561042457600080fd5b5061043f600480360381019061043a9190613c41565b611404565b60405161044c9190613ac2565b60405180910390f35b34801561046157600080fd5b5061047c60048036038101906104779190613b5d565b61147a565b6040516104899190613ac2565b60405180910390f35b34801561049e57600080fd5b506104b960048036038101906104b49190613b5d565b6114ae565b6040516104c69190613ac2565b60405180910390f35b3480156104db57600080fd5b506104f660048036038101906104f19190613b5d565b6114c4565b6040516105039190613d38565b60405180910390f35b61052660048036038101906105219190613dff565b6115cb565b005b34801561053457600080fd5b5061054f600480360381019061054a9190613b5d565b611d31565b60405161055f9493929190613fae565b60405180910390f35b610582600480360381019061057d9190613ffa565b611db1565b60405161058f9190613ac2565b60405180910390f35b3480156105a457600080fd5b506105bf60048036038101906105ba9190613b5d565b611de5565b6040516105cc9190613d38565b60405180910390f35b3480156105e157600080fd5b506105fc60048036038101906105f79190613b5d565b611f51565b6040516106099190613ac2565b60405180910390f35b34801561061e57600080fd5b50610627611f67565b604051610634919061403a565b60405180910390f35b34801561064957600080fd5b50610664600480360381019061065f9190613b5d565b611f8b565b6040516106719190614064565b60405180910390f35b34801561068657600080fd5b506106a1600480360381019061069c919061407f565b611fa1565b005b3480156106af57600080fd5b506106ca60048036038101906106c59190613b5d565b612415565b6040516106d79190613ac2565b60405180910390f35b3480156106ec57600080fd5b5061070760048036038101906107029190613b5d565b612431565b604051610719969594939291906140f0565b60405180910390f35b34801561072e57600080fd5b5061074960048036038101906107449190613b5d565b6124d0565b6040516107569190614268565b60405180910390f35b6107796004803603810190610774919061428a565b6125b9565b6040516107869190613ac2565b60405180910390f35b34801561079b57600080fd5b506107a46125ed565b6040516107b19190613858565b60405180910390f35b3480156107c657600080fd5b506107cf612611565b6040516107dc919061403a565b60405180910390f35b3480156107f157600080fd5b506107fa612635565b60405161080791906142ea565b60405180910390f35b61082a60048036038101906108259190613b5d565b61266e565b6040516108379190613ac2565b60405180910390f35b34801561084c57600080fd5b50610867600480360381019061086291906143b8565b6126a2565b005b34801561087557600080fd5b50610890600480360381019061088b9190613b5d565b612905565b005b34801561089e57600080fd5b506108b960048036038101906108b4919061444d565b612b5d565b6040516108c69190613d38565b60405180910390f35b60006108f5856108de84611051565b8686612c199190939291909392919063ffffffff16565b9050949350505050565b817f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6573ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b81526004016109599190613ac2565b602060405180830381865afa158015610976573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099a91906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a07576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109fe90614544565b60405180910390fd5b6000600160008581526020019081526020016000209050828160000160076101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050565b827f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6573ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b8152600401610ac19190613ac2565b602060405180830381865afa158015610ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0291906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610b6f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b6690614544565b60405180910390fd5b600060016000868152602001908152602001600020905060018160000160016101000a81548160ff021916908315150217905550828160030160008660ff1660ff168152602001908152602001600020600201819055505050505050565b817f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6573ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b8152600401610c279190613ac2565b602060405180830381865afa158015610c44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c6891906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610cd5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ccc90614544565b60405180910390fd5b6000807f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6573ffffffffffffffffffffffffffffffffffffffff16630ea5613f866040518263ffffffff1660e01b8152600401610d319190613ac2565b60c060405180830381865afa158015610d4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7291906145a5565b90919250909150905050809350819250505081841115610dc7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dbe906146ca565b60405180910390fd5b80841015610e0a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e0190614782565b60405180910390fd5b836001600087815260200190815260200160002060000160046101000a81548162ffffff021916908362ffffff1602179055508381146001600087815260200190815260200160002060000160006101000a81548160ff021916908315150217905550847f8445d32a2ee05956c6c842357ca16ee41e92657b1cbcbf1c94f500672e48c3b185604051610e9d9190613ac2565b60405180910390a25050505050565b6000610ecb610eba83611051565b8585612c329290919263ffffffff16565b90509392505050565b60006040517fd0df97cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b827f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6573ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b8152600401610f629190613ac2565b602060405180830381865afa158015610f7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa391906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611010576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161100790614544565b60405180910390fd5b6000600160008681526020019081526020016000209050828160030160008660ff1660ff168152602001908152602001600020600101819055505050505050565b60008160405160200161106491906147ea565b604051602081830303815290604052805190602001209050919050565b6040517fd0df97cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006001600083815260200190815260200160002060000160009054906101000a900460ff169050919050565b847f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6573ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b815260040161113a9190613ac2565b602060405180830381865afa158015611157573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117b91906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146111e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111df90614544565b60405180910390fd5b428563ffffffff1611611230576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161122790614851565b60405180910390fd5b60006001600088815260200190815260200160002090508060000160009054906101000a900460ff1615611299576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611290906148bd565b60405180910390fd5b6000808260000160039054906101000a900460ff1660ff16036112dc576001905060018260000160016101000a81548160ff0219169083151502179055506112fd565b60018260000160039054906101000a900460ff166112fa919061490c565b90505b81600001600381819054906101000a900460ff1660010191906101000a81548160ff021916908360ff1602179055506040518060a001604052808260ff1681526020018560ff1681526020018863ffffffff168152602001878152602001868152508260030160008360ff1660ff16815260200190815260200160002060008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a81548160ff021916908360ff16021790555060408201518160000160026101000a81548163ffffffff021916908363ffffffff16021790555060608201518160010155608082015181600201559050505050505050505050565b60006001600085815260200190815260200160002060020160008460ff1660ff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509392505050565b60006001600083815260200190815260200160002060000160049054906101000a900462ffffff1662ffffff169050919050565b60006114b982611de5565b608001519050919050565b6114cc613688565b60006114d7836124d0565b905060008151905060005b818110156115b6578281815181106114fd576114fc614941565b5b60200260200101516040015163ffffffff1642106115ab57816001826115239190614970565b1061154d5782818151811061153b5761153a614941565b5b602002602001015193505050506115c6565b8260018261155b9190614970565b8151811061156c5761156b614941565b5b60200260200101516040015163ffffffff164210156115aa5782818151811061159857611597614941565b5b602002602001015193505050506115c6565b5b8060010190506114e2565b506115bf613688565b8093505050505b919050565b6115d3612c8a565b60008a905060008a905060008a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050905060006001600085815260200190815260200160002090508060000160009054906101000a900460ff161561168f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611686906149f0565b60405180910390fd5b600061169a856114c4565b9050806000015160ff168260000160029054906101000a900460ff1660ff16146116e05780600001518260000160026101000a81548160ff021916908360ff1602179055505b6000816040015163ffffffff160361172d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161172490614a5c565b60405180910390fd5b8160000160019054906101000a900460ff1661177e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161177590614ac8565b60405180910390fd5b60008461178a87611f51565b6117949190614ae8565b90508034146117d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117cf90614b76565b60405180910390fd5b8260010188886040516117ec929190614bd5565b908152602001604051809103902060009054906101000a900460ff1615611848576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161183f90614c3a565b60405180910390fd5b6118a38360000160079054906101000a900473ffffffffffffffffffffffffffffffffffffffff1633878b8b6040516020016118879493929190614c7b565b6040516020818303038152906040528051906020012086612cd9565b6118e2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016118d990614d02565b60405180910390fd5b6000808d8d8101906118f49190614d37565b915091503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614611966576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161195d90614dc3565b60405180910390fd5b60008082119050600073ffffffffffffffffffffffffffffffffffffffff1661198e8a6114c4565b6060015160001c73ffffffffffffffffffffffffffffffffffffffff1614611a6c576000336040516020016119c391906147ea565b604051602081830303815290604052805190602001209050611a2b8e8e80806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050876060015183612da1565b611a6a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a6190614e2f565b60405180910390fd5b505b8015611b1d578188876002016000886000015160ff1660ff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054611adb9190614970565b1115611b1c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b1390614e9b565b60405180910390fd5b5b6001866001018c8c604051611b33929190614bd5565b908152602001604051809103902060006101000a81548160ff02191690831515021790555087866002016000876000015160ff1660ff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060005b60007f0000000000000000000000003b90746ac1ccbfc5d6ac3a09d9930dc3b224a0f973ffffffffffffffffffffffffffffffffffffffff16630d4d1513338d336040518463ffffffff1660e01b8152600401611c2493929190614ebb565b6020604051808303816000875af1158015611c43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c679190614ef2565b905060018860000160049054906101000a900462ffffff160362ffffff16620f42408281611c9857611c97614f1f565b5b0603611cda5760018860000160006101000a81548160ff02191690831515021790555060008860000160026101000a81548160ff021916908360ff1602179055505b81600101915050888110611bc557611d138a347f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf65612db8565b50505050505050505050611d25612e9b565b50505050505050505050565b60008060606000806001600087815260200190815260200160002090508060000160019054906101000a900460ff169450611d6b86611f51565b93506040518060400160405280600381526020017f4554480000000000000000000000000000000000000000000000000000000000815250925060009150509193509193565b60006040517fd0df97cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ded613688565b60006001600084815260200190815260200160002090506000611e0f846114c4565b90506000816000015160ff1603611e2a578092505050611f4c565b600082600301600060018560000160029054906101000a900460ff16611e50919061490c565b60ff1660ff16815260200190815260200160002060000160009054906101000a900460ff1660ff1614611f3d5781600301600060018460000160029054906101000a900460ff16611ea1919061490c565b60ff1660ff1681526020019081526020016000206040518060a00160405290816000820160009054906101000a900460ff1660ff1660ff1681526020016000820160019054906101000a900460ff1660ff1660ff1681526020016000820160029054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016001820154815260200160028201548152505092505050611f4c565b611f45613688565b8093505050505b919050565b6000611f5c826114c4565b608001519050919050565b7f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6581565b6000611f96826114c4565b600001519050919050565b827f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6573ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b8152600401611ffb9190613ac2565b602060405180830381865afa158015612018573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061203c91906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146120a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120a090614544565b60405180910390fd5b60008360ff16116120ef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120e690614f9a565b60405180910390fd5b600060016000868152602001908152602001600020905060008160000160039054906101000a900460ff1660ff161161215d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161215490615006565b60405180910390fd5b60008363ffffffff16116121a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161219d90614f9a565b60405180910390fd5b428363ffffffff16116121ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121e590615072565b60405180910390fd5b8060000160039054906101000a900460ff1660ff168460ff161115612248576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161223f906150de565b60405180910390fd5b8060000160039054906101000a900460ff1660ff168460ff16036122a857828160030160008660ff1660ff16815260200190815260200160002060000160026101000a81548163ffffffff021916908363ffffffff16021790555061240e565b8060000160039054906101000a900460ff1660ff166001856122ca919061490c565b60ff161161240d5760008160030160006001876122e7919061490c565b60ff1660ff1681526020019081526020016000206040518060a00160405290816000820160009054906101000a900460ff1660ff1660ff1681526020016000820160019054906101000a900460ff1660ff1660ff1681526020016000820160029054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600182015481526020016002820154815250509050806040015163ffffffff168463ffffffff16106123ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016123c59061514a565b60405180910390fd5b838260030160008760ff1660ff16815260200190815260200160002060000160026101000a81548163ffffffff021916908363ffffffff160217905550505b5b5050505050565b600061242082611de5565b6040015163ffffffff169050919050565b60016020528060005260406000206000915090508060000160009054906101000a900460ff16908060000160019054906101000a900460ff16908060000160029054906101000a900460ff16908060000160039054906101000a900460ff16908060000160049054906101000a900462ffffff16908060000160079054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905086565b6060600060016000848152602001908152602001600020905060008160000160039054906101000a900460ff16905060008160ff1667ffffffffffffffff81111561251e5761251d61516a565b5b60405190808252806020026020018201604052801561255757816020015b612544613688565b81526020019060019003908161253c5790505b50905060005b8260ff168160ff1610156125ad576125818660018361257c919061490c565b612b5d565b828260ff168151811061259757612596614941565b5b602002602001018190525080600101905061255d565b50809350505050919050565b60006040517fd0df97cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000181565b7f0000000000000000000000003b90746ac1ccbfc5d6ac3a09d9930dc3b224a0f981565b6040518060400160405280601381526020017f4d756c74694d65726b6c654d696e74657256310000000000000000000000000081525081565b60006040517fd0df97cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b847f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6573ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b81526004016126fc9190613ac2565b602060405180830381865afa158015612719573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273d91906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146127aa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127a190614544565b60405180910390fd5b8282905085859050146127f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127e9906151e5565b60405180910390fd5b60008585905090506000805b5b7f0000000000000000000000003b90746ac1ccbfc5d6ac3a09d9930dc3b224a0f973ffffffffffffffffffffffffffffffffffffffff16630d4d151389898581811061284e5761284d614941565b5b90506020020160208101906128639190613b30565b8b336040518463ffffffff1660e01b815260040161288393929190614ebb565b6020604051808303816000875af11580156128a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128c69190614ef2565b508060010190508585838181106128e0576128df614941565b5b9050602002013581106127ff578160010191508282106127fe57505050505050505050565b807f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6573ffffffffffffffffffffffffffffffffffffffff1663a47d29cb826040518263ffffffff1660e01b815260040161295f9190613ac2565b602060405180830381865afa15801561297c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129a091906144cb565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612a0d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a0490614544565b60405180910390fd5b6000807f000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf6573ffffffffffffffffffffffffffffffffffffffff16630ea5613f856040518263ffffffff1660e01b8152600401612a699190613ac2565b60c060405180830381865afa158015612a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aaa91906145a5565b909192509091509050508093508192505050816001600086815260200190815260200160002060000160046101000a81548162ffffff021916908362ffffff1602179055508181146001600086815260200190815260200160002060000160006101000a81548160ff021916908315150217905550837f8445d32a2ee05956c6c842357ca16ee41e92657b1cbcbf1c94f500672e48c3b183604051612b4f9190613ac2565b60405180910390a250505050565b612b65613688565b6001600084815260200190815260200160002060030160008360ff1660ff1681526020019081526020016000206040518060a00160405290816000820160009054906101000a900460ff1660ff1660ff1681526020016000820160019054906101000a900460ff1660ff1660ff1681526020016000820160029054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160018201548152602001600282015481525050905092915050565b600082612c27868685612c32565b149050949350505050565b60008082905060005b85859050811015612c7e57612c6982878784818110612c5d57612c5c614941565b5b90506020020135612ea5565b91508080612c7690615205565b915050612c3b565b50809150509392505050565b600260005403612ccf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cc690615299565b60405180910390fd5b6002600081905550565b60008073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603612d49576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612d4090615305565b60405180910390fd5b6000612d5484612ed0565b90508473ffffffffffffffffffffffffffffffffffffffff16612d808483612f0090919063ffffffff16565b73ffffffffffffffffffffffffffffffffffffffff16149150509392505050565b600082612dae8584612f27565b1490509392505050565b6000341115612e96576000808334612dd09190615325565b90506000811115612e88573373ffffffffffffffffffffffffffffffffffffffff1681604051612dff9061538a565b60006040518083038185875af1925050503d8060008114612e3c576040519150601f19603f3d011682016040523d82523d6000602084013e612e41565b606091505b50508092505081612e87576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e7e906153eb565b60405180910390fd5b5b612e93858585612f7d565b50505b505050565b6001600081905550565b6000818310612ebd57612eb882846133d8565b612ec8565b612ec783836133d8565b5b905092915050565b600081604051602001612ee39190615478565b604051602081830303815290604052805190602001209050919050565b6000806000612f0f85856133ef565b91509150612f1c81613440565b819250505092915050565b60008082905060005b8451811015612f7257612f5d82868381518110612f5057612f4f614941565b5b6020026020010151612ea5565b91508080612f6a90615205565b915050612f30565b508091505092915050565b60008211156133d35760008060008060008060007f000000000000000000000000000000000000000000000000000000000000000115613114576000808973ffffffffffffffffffffffffffffffffffffffff16638639415b8d8d6040518363ffffffff1660e01b8152600401612ff592919061549e565b61010060405180830381865afa158015613013573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061303791906154c7565b809a50819b50829c50839d50849850859950869e50879f505050505050505050600082111561310d578073ffffffffffffffffffffffffffffffffffffffff16826040516130849061538a565b60006040518083038185875af1925050503d80600081146130c1576040519150601f19603f3d011682016040523d82523d6000602084013e6130c6565b606091505b5050809950508861310c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613103906155c9565b60405180910390fd5b5b50506131a9565b8773ffffffffffffffffffffffffffffffffffffffff16638639415b8b8b6040518363ffffffff1660e01b815260040161314f92919061549e565b60c060405180830381865afa15801561316c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061319091906155e9565b809650819750829850839950849a50859b505050505050505b600086111561325f578473ffffffffffffffffffffffffffffffffffffffff16866040516131d69061538a565b60006040518083038185875af1925050503d8060008114613213576040519150601f19603f3d011682016040523d82523d6000602084013e613218565b606091505b5050809750508661325e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613255906156c2565b60405180910390fd5b5b6000841115613315578273ffffffffffffffffffffffffffffffffffffffff168460405161328c9061538a565b60006040518083038185875af1925050503d80600081146132c9576040519150601f19603f3d011682016040523d82523d6000602084013e6132ce565b606091505b50508097505086613314576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161330b9061572e565b60405180910390fd5b5b60008211156133cb578073ffffffffffffffffffffffffffffffffffffffff16826040516133429061538a565b60006040518083038185875af1925050503d806000811461337f576040519150601f19603f3d011682016040523d82523d6000602084013e613384565b606091505b505080975050866133ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016133c19061579a565b60405180910390fd5b5b505050505050505b505050565b600082600052816020526040600020905092915050565b60008060418351036134305760008060006020860151925060408601519150606086015160001a9050613424878285856135a6565b94509450505050613439565b60006002915091505b9250929050565b60006004811115613454576134536157ba565b5b816004811115613467576134666157ba565b5b03156135a35760016004811115613481576134806157ba565b5b816004811115613494576134936157ba565b5b036134d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016134cb90615835565b60405180910390fd5b600260048111156134e8576134e76157ba565b5b8160048111156134fb576134fa6157ba565b5b0361353b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613532906158a1565b60405180910390fd5b6003600481111561354f5761354e6157ba565b5b816004811115613562576135616157ba565b5b036135a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161359990615933565b60405180910390fd5b5b50565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08360001c11156135e157600060039150915061367f565b6000600187878787604051600081526020016040526040516136069493929190615953565b6020604051602081039080840390855afa158015613628573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036136765760006001925092505061367f565b80600092509250505b94509492505050565b6040518060a00160405280600060ff168152602001600060ff168152602001600063ffffffff16815260200160008019168152602001600081525090565b600080fd5b600080fd5b6000819050919050565b6136e3816136d0565b81146136ee57600080fd5b50565b600081359050613700816136da565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261372b5761372a613706565b5b8235905067ffffffffffffffff8111156137485761374761370b565b5b60208301915083602082028301111561376457613763613710565b5b9250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006137968261376b565b9050919050565b6137a68161378b565b81146137b157600080fd5b50565b6000813590506137c38161379d565b92915050565b600080600080606085870312156137e3576137e26136c6565b5b60006137f1878288016136f1565b945050602085013567ffffffffffffffff811115613812576138116136cb565b5b61381e87828801613715565b93509350506040613831878288016137b4565b91505092959194509250565b60008115159050919050565b6138528161383d565b82525050565b600060208201905061386d6000830184613849565b92915050565b6000819050919050565b61388681613873565b811461389157600080fd5b50565b6000813590506138a38161387d565b92915050565b600080604083850312156138c0576138bf6136c6565b5b60006138ce85828601613894565b92505060206138df858286016137b4565b9150509250929050565b600060ff82169050919050565b6138ff816138e9565b811461390a57600080fd5b50565b60008135905061391c816138f6565b92915050565b60008060006060848603121561393b5761393a6136c6565b5b600061394986828701613894565b935050602061395a8682870161390d565b925050604061396b86828701613894565b9150509250925092565b6000806040838503121561398c5761398b6136c6565b5b600061399a85828601613894565b92505060206139ab85828601613894565b9150509250929050565b6000806000604084860312156139ce576139cd6136c6565b5b600084013567ffffffffffffffff8111156139ec576139eb6136cb565b5b6139f886828701613715565b93509350506020613a0b868287016137b4565b9150509250925092565b613a1e816136d0565b82525050565b6000602082019050613a396000830184613a15565b92915050565b60008060008060608587031215613a5957613a586136c6565b5b6000613a67878288016137b4565b9450506020613a7887828801613894565b935050604085013567ffffffffffffffff811115613a9957613a986136cb565b5b613aa587828801613715565b925092505092959194509250565b613abc81613873565b82525050565b6000602082019050613ad76000830184613ab3565b92915050565b600080600060608486031215613af657613af56136c6565b5b6000613b0486828701613894565b9350506020613b158682870161390d565b9250506040613b26868287016136f1565b9150509250925092565b600060208284031215613b4657613b456136c6565b5b6000613b54848285016137b4565b91505092915050565b600060208284031215613b7357613b726136c6565b5b6000613b8184828501613894565b91505092915050565b600063ffffffff82169050919050565b613ba381613b8a565b8114613bae57600080fd5b50565b600081359050613bc081613b9a565b92915050565b600080600080600060a08688031215613be257613be16136c6565b5b6000613bf088828901613894565b9550506020613c0188828901613bb1565b9450506040613c12888289016136f1565b9350506060613c2388828901613894565b9250506080613c348882890161390d565b9150509295509295909350565b600080600060608486031215613c5a57613c596136c6565b5b6000613c6886828701613894565b9350506020613c798682870161390d565b9250506040613c8a868287016137b4565b9150509250925092565b613c9d816138e9565b82525050565b613cac81613b8a565b82525050565b613cbb816136d0565b82525050565b613cca81613873565b82525050565b60a082016000820151613ce66000850182613c94565b506020820151613cf96020850182613c94565b506040820151613d0c6040850182613ca3565b506060820151613d1f6060850182613cb2565b506080820151613d326080850182613cc1565b50505050565b600060a082019050613d4d6000830184613cd0565b92915050565b60008083601f840112613d6957613d68613706565b5b8235905067ffffffffffffffff811115613d8657613d8561370b565b5b602083019150836001820283011115613da257613da1613710565b5b9250929050565b60008083601f840112613dbf57613dbe613706565b5b8235905067ffffffffffffffff811115613ddc57613ddb61370b565b5b602083019150836001820283011115613df857613df7613710565b5b9250929050565b60008060008060008060008060008060c08b8d031215613e2257613e216136c6565b5b6000613e308d828e01613894565b9a50506020613e418d828e01613894565b99505060408b013567ffffffffffffffff811115613e6257613e616136cb565b5b613e6e8d828e01613d53565b985098505060608b013567ffffffffffffffff811115613e9157613e906136cb565b5b613e9d8d828e01613d53565b965096505060808b013567ffffffffffffffff811115613ec057613ebf6136cb565b5b613ecc8d828e01613715565b945094505060a08b013567ffffffffffffffff811115613eef57613eee6136cb565b5b613efb8d828e01613da9565b92509250509295989b9194979a5092959850565b600081519050919050565b600082825260208201905092915050565b60005b83811015613f49578082015181840152602081019050613f2e565b60008484015250505050565b6000601f19601f8301169050919050565b6000613f7182613f0f565b613f7b8185613f1a565b9350613f8b818560208601613f2b565b613f9481613f55565b840191505092915050565b613fa88161378b565b82525050565b6000608082019050613fc36000830187613849565b613fd06020830186613ab3565b8181036040830152613fe28185613f66565b9050613ff16060830184613f9f565b95945050505050565b60008060408385031215614011576140106136c6565b5b600061401f858286016137b4565b925050602061403085828601613894565b9150509250929050565b600060208201905061404f6000830184613f9f565b92915050565b61405e816138e9565b82525050565b60006020820190506140796000830184614055565b92915050565b600080600060608486031215614098576140976136c6565b5b60006140a686828701613894565b93505060206140b78682870161390d565b92505060406140c886828701613bb1565b9150509250925092565b600062ffffff82169050919050565b6140ea816140d2565b82525050565b600060c0820190506141056000830189613849565b6141126020830188613849565b61411f6040830187614055565b61412c6060830186614055565b61413960808301856140e1565b61414660a0830184613f9f565b979650505050505050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60a0820160008201516141936000850182613c94565b5060208201516141a66020850182613c94565b5060408201516141b96040850182613ca3565b5060608201516141cc6060850182613cb2565b5060808201516141df6080850182613cc1565b50505050565b60006141f1838361417d565b60a08301905092915050565b6000602082019050919050565b600061421582614151565b61421f818561415c565b935061422a8361416d565b8060005b8381101561425b57815161424288826141e5565b975061424d836141fd565b92505060018101905061422e565b5085935050505092915050565b60006020820190508181036000830152614282818461420a565b905092915050565b6000806000604084860312156142a3576142a26136c6565b5b60006142b186828701613894565b935050602084013567ffffffffffffffff8111156142d2576142d16136cb565b5b6142de86828701613715565b92509250509250925092565b600060208201905081810360008301526143048184613f66565b905092915050565b60008083601f84011261432257614321613706565b5b8235905067ffffffffffffffff81111561433f5761433e61370b565b5b60208301915083602082028301111561435b5761435a613710565b5b9250929050565b60008083601f84011261437857614377613706565b5b8235905067ffffffffffffffff8111156143955761439461370b565b5b6020830191508360208202830111156143b1576143b0613710565b5b9250929050565b6000806000806000606086880312156143d4576143d36136c6565b5b60006143e288828901613894565b955050602086013567ffffffffffffffff811115614403576144026136cb565b5b61440f8882890161430c565b9450945050604086013567ffffffffffffffff811115614432576144316136cb565b5b61443e88828901614362565b92509250509295509295909350565b60008060408385031215614464576144636136c6565b5b600061447285828601613894565b92505060206144838582860161390d565b9150509250929050565b60006144988261376b565b9050919050565b6144a88161448d565b81146144b357600080fd5b50565b6000815190506144c58161449f565b92915050565b6000602082840312156144e1576144e06136c6565b5b60006144ef848285016144b6565b91505092915050565b7f4f6e6c7920417274697374000000000000000000000000000000000000000000600082015250565b600061452e600b83613f1a565b9150614539826144f8565b602082019050919050565b6000602082019050818103600083015261455d81614521565b9050919050565b6000815190506145738161387d565b92915050565b6145828161383d565b811461458d57600080fd5b50565b60008151905061459f81614579565b92915050565b60008060008060008060c087890312156145c2576145c16136c6565b5b60006145d089828a01614564565b96505060206145e189828a01614564565b95505060406145f289828a01614590565b945050606061460389828a01614590565b935050608061461489828a01614564565b92505060a061462589828a01614590565b9150509295509295509295565b7f43616e6e6f7420696e6372656173652070726f6a656374206d617820696e766f60008201527f636174696f6e732061626f766520636f726520636f6e7472616374207365742060208201527f70726f6a656374206d617820696e766f636174696f6e73000000000000000000604082015250565b60006146b4605783613f1a565b91506146bf82614632565b606082019050919050565b600060208201905081810360008301526146e3816146a7565b9050919050565b7f43616e6e6f74207365742070726f6a656374206d617820696e766f636174696f60008201527f6e7320746f206c657373207468616e2063757272656e7420696e766f6361746960208201527f6f6e730000000000000000000000000000000000000000000000000000000000604082015250565b600061476c604383613f1a565b9150614777826146ea565b606082019050919050565b6000602082019050818103600083015261479b8161475f565b9050919050565b60008160601b9050919050565b60006147ba826147a2565b9050919050565b60006147cc826147af565b9050919050565b6147e46147df8261378b565b6147c1565b82525050565b60006147f682846147d3565b60148201915081905092915050565b7f4e6f20537461727420426c6f636b20496e205061737400000000000000000000600082015250565b600061483b601683613f1a565b915061484682614805565b602082019050919050565b6000602082019050818103600083015261486a8161482e565b9050919050565b7f4d696e746564204f75742043616e7420437265617465204e6577205374616765600082015250565b60006148a7602083613f1a565b91506148b282614871565b602082019050919050565b600060208201905081810360008301526148d68161489a565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614917826138e9565b9150614922836138e9565b9250828201905060ff81111561493b5761493a6148dd565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600061497b82613873565b915061498683613873565b925082820190508082111561499e5761499d6148dd565b5b92915050565b7f4d617820496e766f636174696f6e732052656163686564000000000000000000600082015250565b60006149da601783613f1a565b91506149e5826149a4565b602082019050919050565b60006020820190508181036000830152614a09816149cd565b9050919050565b7f437265617465204120537461676520546f20426567696e000000000000000000600082015250565b6000614a46601783613f1a565b9150614a5182614a10565b602082019050919050565b60006020820190508181036000830152614a7581614a39565b9050919050565b7f5072696365204e6f7420436f6e66696775726564000000000000000000000000600082015250565b6000614ab2601483613f1a565b9150614abd82614a7c565b602082019050919050565b60006020820190508181036000830152614ae181614aa5565b9050919050565b6000614af382613873565b9150614afe83613873565b9250828202614b0c81613873565b91508282048414831517614b2357614b226148dd565b5b5092915050565b7f57726f6e67205072696365000000000000000000000000000000000000000000600082015250565b6000614b60600b83613f1a565b9150614b6b82614b2a565b602082019050919050565b60006020820190508181036000830152614b8f81614b53565b9050919050565b600081905092915050565b82818337600083830152505050565b6000614bbc8385614b96565b9350614bc9838584614ba1565b82840190509392505050565b6000614be2828486614bb0565b91508190509392505050565b7f4e6f6e6365205573656400000000000000000000000000000000000000000000600082015250565b6000614c24600a83613f1a565b9150614c2f82614bee565b602082019050919050565b60006020820190508181036000830152614c5381614c17565b9050919050565b6000819050919050565b614c75614c7082613873565b614c5a565b82525050565b6000614c8782876147d3565b601482019150614c978286614c64565b602082019150614ca8828486614bb0565b915081905095945050505050565b7f496e76616c6964205369676e6174757265000000000000000000000000000000600082015250565b6000614cec601183613f1a565b9150614cf782614cb6565b602082019050919050565b60006020820190508181036000830152614d1b81614cdf565b9050919050565b600081359050614d318161449f565b92915050565b60008060408385031215614d4e57614d4d6136c6565b5b6000614d5c85828601614d22565b9250506020614d6d85828601613894565b9150509250929050565b7f4261642044617461000000000000000000000000000000000000000000000000600082015250565b6000614dad600883613f1a565b9150614db882614d77565b602082019050919050565b60006020820190508181036000830152614ddc81614da0565b9050919050565b7f4e6f74204f6e20416c6c6f776c69737400000000000000000000000000000000600082015250565b6000614e19601083613f1a565b9150614e2482614de3565b602082019050919050565b60006020820190508181036000830152614e4881614e0c565b9050919050565b7f43616e2774204d696e742054686174204d616e79000000000000000000000000600082015250565b6000614e85601483613f1a565b9150614e9082614e4f565b602082019050919050565b60006020820190508181036000830152614eb481614e78565b9050919050565b6000606082019050614ed06000830186613f9f565b614edd6020830185613ab3565b614eea6040830184613f9f565b949350505050565b600060208284031215614f0857614f076136c6565b5b6000614f1684828501614564565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f43616e2774206265203000000000000000000000000000000000000000000000600082015250565b6000614f84600a83613f1a565b9150614f8f82614f4e565b602082019050919050565b60006020820190508181036000830152614fb381614f77565b9050919050565b7f4e6f2053746167657320657869737420666f722070726f6a6563740000000000600082015250565b6000614ff0601b83613f1a565b9150614ffb82614fba565b602082019050919050565b6000602082019050818103600083015261501f81614fe3565b9050919050565b7f4d75737420626520696e20746865206675747572650000000000000000000000600082015250565b600061505c601583613f1a565b915061506782615026565b602082019050919050565b6000602082019050818103600083015261508b8161504f565b9050919050565b7f537461676520646f65736e277420657869737400000000000000000000000000600082015250565b60006150c8601383613f1a565b91506150d382615092565b602082019050919050565b600060208201905081810360008301526150f7816150bb565b9050919050565b7f53746167652063616e2774207374617274206166746572206e657874206f6e65600082015250565b6000615134602083613f1a565b915061513f826150fe565b602082019050919050565b6000602082019050818103600083015261516381615127565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f556e657175616c20617272617973000000000000000000000000000000000000600082015250565b60006151cf600e83613f1a565b91506151da82615199565b602082019050919050565b600060208201905081810360008301526151fe816151c2565b9050919050565b600061521082613873565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615242576152416148dd565b5b600182019050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b6000615283601f83613f1a565b915061528e8261524d565b602082019050919050565b600060208201905081810360008301526152b281615276565b9050919050565b7f4d697373696e672053797374656d204164647265737300000000000000000000600082015250565b60006152ef601683613f1a565b91506152fa826152b9565b602082019050919050565b6000602082019050818103600083015261531e816152e2565b9050919050565b600061533082613873565b915061533b83613873565b9250828203905081811115615353576153526148dd565b5b92915050565b600081905092915050565b50565b6000615374600083615359565b915061537f82615364565b600082019050919050565b600061539582615367565b9150819050919050565b7f526566756e64206661696c656400000000000000000000000000000000000000600082015250565b60006153d5600d83613f1a565b91506153e08261539f565b602082019050919050565b60006020820190508181036000830152615404816153c8565b9050919050565b7f19457468657265756d205369676e6564204d6573736167653a0a333200000000600082015250565b6000615441601c83614b96565b915061544c8261540b565b601c82019050919050565b6000819050919050565b61547261546d826136d0565b615457565b82525050565b600061548382615434565b915061548f8284615461565b60208201915081905092915050565b60006040820190506154b36000830185613ab3565b6154c06020830184613ab3565b9392505050565b600080600080600080600080610100898b0312156154e8576154e76136c6565b5b60006154f68b828c01614564565b98505060206155078b828c016144b6565b97505060406155188b828c01614564565b96505060606155298b828c016144b6565b955050608061553a8b828c01614564565b94505060a061554b8b828c016144b6565b93505060c061555c8b828c01614564565b92505060e061556d8b828c016144b6565b9150509295985092959890939650565b7f506c6174666f726d2050726f7669646572207061796d656e74206661696c6564600082015250565b60006155b3602083613f1a565b91506155be8261557d565b602082019050919050565b600060208201905081810360008301526155e2816155a6565b9050919050565b60008060008060008060c08789031215615606576156056136c6565b5b600061561489828a01614564565b965050602061562589828a016144b6565b955050604061563689828a01614564565b945050606061564789828a016144b6565b935050608061565889828a01614564565b92505060a061566989828a016144b6565b9150509295509295509295565b7f52656e6465722050726f7669646572207061796d656e74206661696c65640000600082015250565b60006156ac601e83613f1a565b91506156b782615676565b602082019050919050565b600060208201905081810360008301526156db8161569f565b9050919050565b7f417274697374207061796d656e74206661696c65640000000000000000000000600082015250565b6000615718601583613f1a565b9150615723826156e2565b602082019050919050565b600060208201905081810360008301526157478161570b565b9050919050565b7f4164646974696f6e616c205061796565207061796d656e74206661696c656400600082015250565b6000615784601f83613f1a565b915061578f8261574e565b602082019050919050565b600060208201905081810360008301526157b381615777565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f45434453413a20696e76616c6964207369676e61747572650000000000000000600082015250565b600061581f601883613f1a565b915061582a826157e9565b602082019050919050565b6000602082019050818103600083015261584e81615812565b9050919050565b7f45434453413a20696e76616c6964207369676e6174757265206c656e67746800600082015250565b600061588b601f83613f1a565b915061589682615855565b602082019050919050565b600060208201905081810360008301526158ba8161587e565b9050919050565b7f45434453413a20696e76616c6964207369676e6174757265202773272076616c60008201527f7565000000000000000000000000000000000000000000000000000000000000602082015250565b600061591d602283613f1a565b9150615928826158c1565b604082019050919050565b6000602082019050818103600083015261594c81615910565b9050919050565b60006080820190506159686000830187613a15565b6159756020830186614055565b6159826040830185613a15565b61598f6060830184613a15565b9594505050505056fea2646970667358221220c51bb48a70916c9af3acc634454b189186eb302e1c2185c6726f2df957adb4e664736f6c63430008110033

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

000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf650000000000000000000000003b90746ac1ccbfc5d6ac3a09d9930dc3b224a0f9

-----Decoded View---------------
Arg [0] : _genArt721Address (address): 0x959d2F3cAF19d20BDBb4e0A4f21cA8A815EDDF65
Arg [1] : _minterFilter (address): 0x3b90746ac1ccBFC5D6aC3a09d9930Dc3B224a0F9

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000959d2f3caf19d20bdbb4e0a4f21ca8a815eddf65
Arg [1] : 0000000000000000000000003b90746ac1ccbfc5d6ac3a09d9930dc3b224a0f9


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.