ETH Price: $3,921.33 (+0.59%)

Contract

0xc74eC888104842277Fa1b74e1C3D415eb673009F
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Approval For...213117722024-12-02 2:13:4712 days ago1733105627IN
0xc74eC888...eb673009F
0 ETH0.0005969712.88935414
Set Approval For...212877792024-11-28 17:46:2315 days ago1732815983IN
0xc74eC888...eb673009F
0 ETH0.0006702714.47206157
Set Approval For...212278322024-11-20 8:43:3523 days ago1732092215IN
0xc74eC888...eb673009F
0 ETH0.000433899.36843316
Set Approval For...205414842024-08-16 13:26:59119 days ago1723814819IN
0xc74eC888...eb673009F
0 ETH0.000144813.12675209
Safe Transfer Fr...205405702024-08-16 10:23:11119 days ago1723803791IN
0xc74eC888...eb673009F
0 ETH0.000186623.08380092
Set Approval For...204850892024-08-08 16:33:35127 days ago1723134815IN
0xc74eC888...eb673009F
0 ETH0.0006021422.73342412
Set Approval For...204850672024-08-08 16:29:11127 days ago1723134551IN
0xc74eC888...eb673009F
0 ETH0.0008914619.21807286
Set Approval For...204202682024-07-30 15:29:11136 days ago1722353351IN
0xc74eC888...eb673009F
0 ETH0.0007468316.12507345
Set Approval For...203445752024-07-20 1:51:35147 days ago1721440295IN
0xc74eC888...eb673009F
0 ETH0.00014743.18260683
Set Approval For...201661122024-06-25 3:48:23172 days ago1719287303IN
0xc74eC888...eb673009F
0 ETH0.000224614.8496298
Set Approval For...200565202024-06-09 20:01:11187 days ago1717963271IN
0xc74eC888...eb673009F
0 ETH0.000344897.44668032
Set Approval For...199683052024-05-28 12:15:59199 days ago1716898559IN
0xc74eC888...eb673009F
0 ETH0.0007881817.01799431
Set Approval For...197853972024-05-02 22:24:47225 days ago1714688687IN
0xc74eC888...eb673009F
0 ETH0.000238465.14872803
Transfer From197142612024-04-22 23:37:23235 days ago1713829043IN
0xc74eC888...eb673009F
0 ETH0.000425567.35237928
Set Approval For...194878202024-03-22 4:35:11267 days ago1711082111IN
0xc74eC888...eb673009F
0 ETH0.0011153824.08248754
Set Approval For...193408182024-03-01 14:07:47287 days ago1709302067IN
0xc74eC888...eb673009F
0 ETH0.0031670868.27517314
Set Approval For...192714832024-02-20 21:06:59297 days ago1708463219IN
0xc74eC888...eb673009F
0 ETH0.0015134432.67715083
Set Approval For...192381132024-02-16 4:33:11302 days ago1708057991IN
0xc74eC888...eb673009F
0 ETH0.0008336718
Safe Transfer Fr...190934482024-01-26 21:27:11322 days ago1706304431IN
0xc74eC888...eb673009F
0 ETH0.0008805714.53523232
Set Approval For...190361342024-01-18 20:29:11330 days ago1705609751IN
0xc74eC888...eb673009F
0 ETH0.0016823336.32376925
Set Approval For...190203972024-01-16 15:38:23332 days ago1705419503IN
0xc74eC888...eb673009F
0 ETH0.0017188337.11183667
Set Approval For...189950842024-01-13 2:46:59336 days ago1705114019IN
0xc74eC888...eb673009F
0 ETH0.000758716.38149198
Safe Transfer Fr...189950782024-01-13 2:45:47336 days ago1705113947IN
0xc74eC888...eb673009F
0 ETH0.0010321615.78670115
Transfer From189873492024-01-12 0:49:35337 days ago1705020575IN
0xc74eC888...eb673009F
0 ETH0.0007061717.31639407
Set Approval For...189714792024-01-09 19:26:35339 days ago1704828395IN
0xc74eC888...eb673009F
0 ETH0.0007427416.0368571
View all transactions

Latest 13 internal transactions

Advanced mode:
Parent Transaction Hash Block
From
To
186917902023-12-01 13:14:11378 days ago1701436451
0xc74eC888...eb673009F
 Contract Creation0 ETH
186917662023-12-01 13:09:23378 days ago1701436163
0xc74eC888...eb673009F
 Contract Creation0 ETH
186854662023-11-30 15:59:35379 days ago1701359975
0xc74eC888...eb673009F
 Contract Creation0 ETH
186854612023-11-30 15:58:35379 days ago1701359915
0xc74eC888...eb673009F
 Contract Creation0 ETH
186825202023-11-30 6:04:59380 days ago1701324299
0xc74eC888...eb673009F
 Contract Creation0 ETH
186811192023-11-30 1:23:35380 days ago1701307415
0xc74eC888...eb673009F
 Contract Creation0 ETH
186803972023-11-29 22:58:23380 days ago1701298703
0xc74eC888...eb673009F
 Contract Creation0 ETH
186602882023-11-27 3:25:47383 days ago1701055547
0xc74eC888...eb673009F
 Contract Creation0 ETH
186602522023-11-27 3:18:35383 days ago1701055115
0xc74eC888...eb673009F
 Contract Creation0 ETH
186307982023-11-23 0:16:35387 days ago1700698595
0xc74eC888...eb673009F
 Contract Creation0 ETH
186305792023-11-22 23:32:47387 days ago1700695967
0xc74eC888...eb673009F
 Contract Creation0 ETH
186305332023-11-22 23:23:23387 days ago1700695403
0xc74eC888...eb673009F
 Contract Creation0 ETH
186305312023-11-22 23:22:59387 days ago1700695379
0xc74eC888...eb673009F
 Contract Creation0 ETH
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x5fdf5E6C...07A7e1389
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
GenArt721CoreV3_Engine_Flex

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 25 runs

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

// Created By: Art Blocks Inc.

import "../../interfaces/v0.8.x/IRandomizer_V3CoreBase.sol";
import "../../interfaces/v0.8.x/IAdminACLV0.sol";
import "../../interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_Flex.sol";
import "../../interfaces/v0.8.x/IGenArt721CoreContractExposesHashSeed.sol";
import "../../interfaces/v0.8.x/IDependencyRegistryCompatibleV0.sol";
import "../../interfaces/v0.8.x/IManifold.sol";

import "@openzeppelin-4.7/contracts/access/Ownable.sol";
import "../../libs/v0.8.x/ERC721_PackedHashSeed.sol";
import "../../libs/v0.8.x/BytecodeStorageV1.sol";
import "../../libs/v0.8.x/Bytes32Strings.sol";

/**
 * @title Art Blocks Engine Flex ERC-721 core contract, V3.
 * @author Art Blocks Inc.
 * @notice Privileged Roles and Ownership:
 * This contract is designed to be managed, with progressively limited powers
 * as a project progresses from active to locked.
 * Privileged roles and abilities are controlled by the admin ACL contract and
 * artists. Both of these roles hold extensive power and can arbitrarily
 * control and modify portions of projects, dependent upon project state. After
 * a project is locked, important project metadata fields are locked including
 * the project name, artist name, and script and display details. Edition size
 * can never be increased.
 * 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 the Admin ACL contract:
 * - updateArtblocksDependencyRegistryAddress
 * - updateProviderSalesAddresses
 * - updateProviderPrimarySalesPercentages (up to 100%)
 * - updateProviderSecondarySalesBPS (up to 100%)
 * - updateMinterContract
 * - updateRandomizerAddress
 * - toggleProjectIsActive
 * - addProject
 * - forbidNewProjects (forever forbidding new projects)
 * - updateDefaultBaseURI (used to initialize new project base URIs)
 * - updateIPFSGateway
 * - updateArweaveGateway
 * ----------------------------------------------------------------------------
 * The following functions are restricted to either the the Artist address or
 * the Admin ACL contract, only when the project is not locked:
 * - updateProjectName
 * - updateProjectArtistName
 * - updateProjectLicense
 * - Change project script via addProjectScript, updateProjectScript,
 *   and removeProjectLastScript
 * - updateProjectScriptType
 * - updateProjectAspectRatio
 * ----------------------------------------------------------------------------
 * The following functions are restricted to only the Artist address:
 * - proposeArtistPaymentAddressesAndSplits (Note that this has to be accepted
 *   by adminAcceptArtistAddressesAndSplits to take effect, which is restricted
 *   to the Admin ACL contract, or the artist if the core contract owner has
 *   renounced ownership. Also note that a proposal will be automatically
 *   accepted if the artist only proposes changed payee percentages without
 *   modifying any payee addresses, or is only removing payee addresses, or
 *   if the global config `autoApproveArtistSplitProposals` is set to `true`.)
 * - toggleProjectIsPaused (note the artist can still mint while paused)
 * - updateProjectSecondaryMarketRoyaltyPercentage (up to
     ARTIST_MAX_SECONDARY_ROYALTY_PERCENTAGE percent)
 * - updateProjectWebsite
 * - updateProjectMaxInvocations (to a number greater than or equal to the
 *   current number of invocations, and less than current project maximum
 *   invocations)
 * - updateProjectBaseURI (controlling the base URI for tokens in the project)
 * ----------------------------------------------------------------------------
 * The following function is restricted to either the Admin ACL contract, or
 * the Artist address if the core contract owner has renounced ownership:
 * - adminAcceptArtistAddressesAndSplits
 * - updateProjectArtistAddress (owner ultimately controlling the project and
 *   its and-on revenue, unless owner has renounced ownership)
 * ----------------------------------------------------------------------------
 * The following function is restricted to the artist when a project is
 * unlocked, and only callable by Admin ACL contract when a project is locked:
 * - updateProjectDescription
 * ----------------------------------------------------------------------------
 * The following functions for managing external asset dependencies are restricted
 * to projects with external asset dependencies that are unlocked:
 * - lockProjectExternalAssetDependencies 
 * - updateProjectExternalAssetDependency
 * - removeProjectExternalAssetDependency
 * - addProjectExternalAssetDependency
 * ----------------------------------------------------------------------------
 * The following function is restricted to owner calling directly:
 * - transferOwnership
 * - renounceOwnership
 * ----------------------------------------------------------------------------
 * The following configuration variables are set at time of contract deployment,
 * and not modifiable thereafter (immutable after the point of deployment):
 * - (bool) autoApproveArtistSplitProposals
 * ----------------------------------------------------------------------------
 * Additional admin and artist privileged roles may be described on minters,
 * registries, and other contracts that may interact with this core contract.
 */
contract GenArt721CoreV3_Engine_Flex is
    ERC721_PackedHashSeed,
    Ownable,
    IDependencyRegistryCompatibleV0,
    IManifold,
    IGenArt721CoreContractV3_Engine_Flex,
    IGenArt721CoreContractExposesHashSeed
{
    using BytecodeStorageWriter for string;
    using Bytes32Strings for bytes32;
    uint256 constant ONE_HUNDRED = 100;
    uint256 constant ONE_MILLION = 1_000_000;
    uint24 constant ONE_MILLION_UINT24 = 1_000_000;
    uint256 constant FOUR_WEEKS_IN_SECONDS = 2_419_200;
    uint8 constant AT_CHARACTER_CODE = uint8(bytes1("@")); // 0x40

    // numeric constants
    uint256 constant MAX_PROVIDER_SECONDARY_SALES_BPS = 10000; // 10_000 BPS = 100%
    uint256 constant ARTIST_MAX_SECONDARY_ROYALTY_PERCENTAGE = 95; // 95%

    // This contract emits generic events that contain fields that indicate
    // which parameter has been updated. This is sufficient for application
    // state management, while also simplifying the contract and indexing code.
    // This was done as an alternative to having custom events that emit what
    // field-values have changed for each event, given that changed values can
    // be introspected by indexers due to the design of this smart contract
    // exposing these state changes via publicly viewable fields.
    //
    // The following fields are used to indicate which contract-level parameter
    // has been updated in the `PlatformUpdated` event:
    bytes32 constant FIELD_NEXT_PROJECT_ID = "nextProjectId";
    bytes32 constant FIELD_NEW_PROJECTS_FORBIDDEN = "newProjectsForbidden";
    bytes32 constant FIELD_DEFAULT_BASE_URI = "defaultBaseURI";
    bytes32 constant FIELD_RANDOMIZER_ADDRESS = "randomizerAddress";
    bytes32 constant FIELD_ARTBLOCKS_DEPENDENCY_REGISTRY_ADDRESS =
        "dependencyRegistryAddress";
    bytes32 constant FIELD_PROVIDER_SALES_ADDRESSES = "providerSalesAddresses";
    bytes32 constant FIELD_PROVIDER_PRIMARY_SALES_PERCENTAGES =
        "providerPrimaryPercentages";
    bytes32 constant FIELD_PROVIDER_SECONDARY_SALES_BPS =
        "providerSecondaryBPS";
    // The following fields are used to indicate which project-level parameter
    // has been updated in the `ProjectUpdated` event:
    bytes32 constant FIELD_PROJECT_COMPLETED = "completed";
    bytes32 constant FIELD_PROJECT_ACTIVE = "active";
    bytes32 constant FIELD_PROJECT_ARTIST_ADDRESS = "artistAddress";
    bytes32 constant FIELD_PROJECT_PAUSED = "paused";
    bytes32 constant FIELD_PROJECT_CREATED = "created";
    bytes32 constant FIELD_PROJECT_NAME = "name";
    bytes32 constant FIELD_PROJECT_ARTIST_NAME = "artistName";
    bytes32 constant FIELD_PROJECT_SECONDARY_MARKET_ROYALTY_PERCENTAGE =
        "royaltyPercentage";
    bytes32 constant FIELD_PROJECT_DESCRIPTION = "description";
    bytes32 constant FIELD_PROJECT_WEBSITE = "website";
    bytes32 constant FIELD_PROJECT_LICENSE = "license";
    bytes32 constant FIELD_PROJECT_MAX_INVOCATIONS = "maxInvocations";
    bytes32 constant FIELD_PROJECT_SCRIPT = "script";
    bytes32 constant FIELD_PROJECT_SCRIPT_TYPE = "scriptType";
    bytes32 constant FIELD_PROJECT_ASPECT_RATIO = "aspectRatio";
    bytes32 constant FIELD_PROJECT_BASE_URI = "baseURI";

    /// Dependency registry managed by Art Blocks
    address public artblocksDependencyRegistryAddress;

    /// current randomizer contract
    IRandomizer_V3CoreBase public randomizerContract;

    /// append-only array of all randomizer contract addresses ever used by
    /// this contract
    address[] private _historicalRandomizerAddresses;

    /// admin ACL contract
    IAdminACLV0 public adminACLContract;

    struct Project {
        uint24 invocations;
        uint24 maxInvocations;
        uint24 scriptCount;
        // max uint64 ~= 1.8e19 sec ~= 570 billion years
        uint64 completedTimestamp;
        bool active;
        bool paused;
        string name;
        string artist;
        address descriptionAddress;
        string website;
        string license;
        string projectBaseURI;
        bytes32 scriptTypeAndVersion;
        string aspectRatio;
        // mapping from script index to address storing script in bytecode
        mapping(uint256 => address) scriptBytecodeAddresses;
        bool externalAssetDependenciesLocked;
        uint24 externalAssetDependencyCount;
        mapping(uint256 => ExternalAssetDependency) externalAssetDependencies;
    }

    mapping(uint256 => Project) projects;

    string public preferredIPFSGateway;
    string public preferredArweaveGateway;

    /// packed struct containing project financial information
    struct ProjectFinance {
        address payable additionalPayeePrimarySales;
        // packed uint: max of 95, max uint8 = 255
        uint8 secondaryMarketRoyaltyPercentage;
        address payable additionalPayeeSecondarySales;
        // packed uint: max of 100, max uint8 = 255
        uint8 additionalPayeeSecondarySalesPercentage;
        address payable artistAddress;
        // packed uint: max of 100, max uint8 = 255
        uint8 additionalPayeePrimarySalesPercentage;
    }
    // Project financials mapping
    mapping(uint256 => ProjectFinance) projectIdToFinancials;

    /// hash of artist's proposed payment updates to be approved by admin
    mapping(uint256 => bytes32) public proposedArtistAddressesAndSplitsHash;

    /// The render provider payment address for all primary sales revenues
    /// (packed)
    address payable public renderProviderPrimarySalesAddress;
    /// Percentage of primary sales revenue allocated to the render provider
    /// (packed)
    // packed uint: max of 100, max uint8 = 255
    uint8 private _renderProviderPrimarySalesPercentage = 10;
    /// The platform provider payment address for all primary sales revenues
    /// (packed)
    address payable public platformProviderPrimarySalesAddress;
    /// Percentage of primary sales revenue allocated to the platform provider
    /// (packed)
    // packed uint: max of 100, max uint8 = 255
    uint8 private _platformProviderPrimarySalesPercentage = 10;

    /// The render provider payment address for all secondary sales royalty
    /// revenues
    address payable public renderProviderSecondarySalesAddress;
    /// Basis Points of secondary sales royalties allocated to the
    /// render provider
    uint256 public renderProviderSecondarySalesBPS = 250;
    /// The platform provider payment address for all secondary sales royalty
    /// revenues
    address payable public platformProviderSecondarySalesAddress;
    /// Basis Points of secondary sales royalties allocated to the
    /// platform provider
    uint256 public platformProviderSecondarySalesBPS = 250;

    /// single minter allowed for this core contract
    address public minterContract;

    /// starting (initial) project ID on this contract
    uint256 public immutable startingProjectId;

    /// next project ID to be created
    uint248 private _nextProjectId;

    /// bool indicating if adding new projects is forbidden;
    /// default behavior is to allow new projects
    bool public newProjectsForbidden;

    /// configuration variable (determined at time of deployment)
    /// that determines whether or not admin approval^ should be required
    /// to accept artist address change proposals, or if these proposals
    /// should always auto-approve, as determined by the business process
    /// requirements of the Engine partner using this contract.
    ///
    /// ^does not apply in the case where contract-ownership itself is revoked
    bool public immutable autoApproveArtistSplitProposals;

    /// version & type of this core contract
    bytes32 constant CORE_VERSION = "v3.1.4";

    function coreVersion() external pure returns (string memory) {
        return CORE_VERSION.toString();
    }

    bytes32 constant CORE_TYPE = "GenArt721CoreV3_Engine_Flex";

    function coreType() external pure returns (string memory) {
        return CORE_TYPE.toString();
    }

    /// default base URI to initialize all new project projectBaseURI values to
    string public defaultBaseURI;

    function _onlyUnlockedProjectExternalAssetDependencies(
        uint256 _projectId
    ) internal view {
        require(
            !projects[_projectId].externalAssetDependenciesLocked,
            "External dependencies locked"
        );
    }

    function _onlyNonZeroAddress(address _address) internal pure {
        require(_address != address(0), "Must input non-zero address");
    }

    function _onlyNonEmptyString(string memory _string) internal pure {
        require(bytes(_string).length != 0, "Must input non-empty string");
    }

    function _onlyValidTokenId(uint256 _tokenId) internal view {
        require(_exists(_tokenId), "Token ID does not exist");
    }

    function _onlyValidProjectId(uint256 _projectId) internal view {
        require(
            (_projectId >= startingProjectId) && (_projectId < _nextProjectId),
            "Project ID does not exist"
        );
    }

    function _onlyUnlocked(uint256 _projectId) internal view {
        // Note: calling `_projectUnlocked` enforces that the `_projectId`
        //       passed in is valid.`
        require(_projectUnlocked(_projectId), "Only if unlocked");
    }

    function _onlyAdminACL(bytes4 _selector) internal {
        require(
            adminACLAllowed(msg.sender, address(this), _selector),
            "Only Admin ACL allowed"
        );
    }

    function _onlyArtist(uint256 _projectId) internal view {
        require(
            msg.sender == projectIdToFinancials[_projectId].artistAddress,
            "Only artist"
        );
    }

    function _onlyArtistOrAdminACL(
        uint256 _projectId,
        bytes4 _selector
    ) internal {
        require(
            msg.sender == projectIdToFinancials[_projectId].artistAddress ||
                adminACLAllowed(msg.sender, address(this), _selector),
            "Only artist or Admin ACL allowed"
        );
    }

    /**
     * This modifier allows the artist of a project to call a function if the
     * owner of the contract has renounced ownership. This is to allow the
     * contract to continue to function if the owner decides to renounce
     * ownership.
     */
    function _onlyAdminACLOrRenouncedArtist(
        uint256 _projectId,
        bytes4 _selector
    ) internal {
        require(
            adminACLAllowed(msg.sender, address(this), _selector) ||
                (owner() == address(0) &&
                    msg.sender ==
                    projectIdToFinancials[_projectId].artistAddress),
            "Only Admin ACL allowed, or artist if owner has renounced"
        );
    }

    /**
     * @notice Initializes contract.
     * @param _tokenName Name of token.
     * @param _tokenSymbol Token symbol.
     * @param _randomizerContract Randomizer contract.
     * @param _adminACLContract Address of admin access control contract, to be
     * set as contract owner.
     * @param _startingProjectId The initial next project ID.
     * @param _autoApproveArtistSplitProposals Whether or not to always
     * auto-approve proposed artist split updates.
     * @dev _startingProjectId should be set to a value much, much less than
     * max(uint248), but an explicit input type of `uint248` is used as it is
     * safer to cast up to `uint256` than it is to cast down for the purposes
     * of setting `_nextProjectId`.
     */
    constructor(
        string memory _tokenName,
        string memory _tokenSymbol,
        address _renderProviderAddress,
        address _platformProviderAddress,
        address _randomizerContract,
        address _adminACLContract,
        uint248 _startingProjectId,
        bool _autoApproveArtistSplitProposals
    ) ERC721_PackedHashSeed(_tokenName, _tokenSymbol) {
        _onlyNonZeroAddress(_renderProviderAddress);
        _onlyNonZeroAddress(_platformProviderAddress);
        _onlyNonZeroAddress(_randomizerContract);
        _onlyNonZeroAddress(_adminACLContract);
        // setup immutable `autoApproveArtistSplitProposals` config
        autoApproveArtistSplitProposals = _autoApproveArtistSplitProposals;
        // record contracts starting project ID
        // casting-up is safe
        startingProjectId = uint256(_startingProjectId);
        _updateProviderSalesAddresses(
            _renderProviderAddress,
            _renderProviderAddress,
            _platformProviderAddress,
            _platformProviderAddress
        );
        _updateRandomizerAddress(_randomizerContract);
        // set AdminACL management contract as owner
        _transferOwnership(_adminACLContract);
        // initialize default base URI
        _updateDefaultBaseURI(
            string.concat(
                "https://token.artblocks.io/",
                toHexString(address(this)),
                "/"
            )
        );
        // initialize next project ID
        _nextProjectId = _startingProjectId;
        emit PlatformUpdated(FIELD_NEXT_PROJECT_ID);
        // @dev follow-on action: This contract does not self-register. A core
        // registry owner must register contract in a subsequent call.
    }

    /**
     * @notice Updates preferredIPFSGateway to `_gateway`.
     */
    function updateIPFSGateway(string calldata _gateway) public {
        _onlyAdminACL(this.updateIPFSGateway.selector);
        preferredIPFSGateway = _gateway;
        emit GatewayUpdated(ExternalAssetDependencyType.IPFS, _gateway);
    }

    /**
     * @notice Updates preferredArweaveGateway to `_gateway`.
     */
    function updateArweaveGateway(string calldata _gateway) public {
        _onlyAdminACL(this.updateArweaveGateway.selector);
        preferredArweaveGateway = _gateway;
        emit GatewayUpdated(ExternalAssetDependencyType.ARWEAVE, _gateway);
    }

    /**
     * @notice Locks external asset dependencies for project `_projectId`.
     */
    function lockProjectExternalAssetDependencies(uint256 _projectId) external {
        _onlyUnlockedProjectExternalAssetDependencies(_projectId);
        _onlyArtistOrAdminACL(
            _projectId,
            this.lockProjectExternalAssetDependencies.selector
        );
        projects[_projectId].externalAssetDependenciesLocked = true;
        emit ProjectExternalAssetDependenciesLocked(_projectId);
    }

    /**
     * @notice Updates external asset dependency for project `_projectId`.
     * @param _projectId Project to be updated.
     * @param _index Asset index.
     * @param _cidOrData Asset cid (Content identifier) or data string to be translated into bytecode.
     * @param _dependencyType Asset dependency type.
     *  0 - IPFS
     *  1 - ARWEAVE
     *  2 - ONCHAIN
     */
    function updateProjectExternalAssetDependency(
        uint256 _projectId,
        uint256 _index,
        string memory _cidOrData,
        ExternalAssetDependencyType _dependencyType
    ) external {
        _onlyUnlockedProjectExternalAssetDependencies(_projectId);
        _onlyArtistOrAdminACL(
            _projectId,
            this.updateProjectExternalAssetDependency.selector
        );
        uint24 assetCount = projects[_projectId].externalAssetDependencyCount;
        require(_index < assetCount, "Asset index out of range");
        ExternalAssetDependency storage _oldDependency = projects[_projectId]
            .externalAssetDependencies[_index];
        ExternalAssetDependencyType _oldDependencyType = _oldDependency
            .dependencyType;
        projects[_projectId]
            .externalAssetDependencies[_index]
            .dependencyType = _dependencyType;
        // if the incoming dependency type is onchain, we need to write the data to bytecode
        if (_dependencyType == ExternalAssetDependencyType.ONCHAIN) {
            if (_oldDependencyType != ExternalAssetDependencyType.ONCHAIN) {
                // we only need to set the cid to an empty string if we are replacing an offchain asset
                // an onchain asset will already have an empty cid
                projects[_projectId].externalAssetDependencies[_index].cid = "";
            }

            projects[_projectId]
                .externalAssetDependencies[_index]
                .bytecodeAddress = _cidOrData.writeToBytecode();
            // we don't want to emit data, so we emit the cid as an empty string
            _cidOrData = "";
        } else {
            projects[_projectId]
                .externalAssetDependencies[_index]
                .cid = _cidOrData;
        }
        emit ExternalAssetDependencyUpdated(
            _projectId,
            _index,
            _cidOrData,
            _dependencyType,
            assetCount
        );
    }

    /**
     * @notice Removes external asset dependency for project `_projectId` at index `_index`.
     * Removal is done by swapping the element to be removed with the last element in the array, then deleting this last element.
     * Assets with indices higher than `_index` can have their indices adjusted as a result of this operation.
     * @param _projectId Project to be updated.
     * @param _index Asset index
     */
    function removeProjectExternalAssetDependency(
        uint256 _projectId,
        uint256 _index
    ) external {
        _onlyUnlockedProjectExternalAssetDependencies(_projectId);
        _onlyArtistOrAdminACL(
            _projectId,
            this.removeProjectExternalAssetDependency.selector
        );
        uint24 assetCount = projects[_projectId].externalAssetDependencyCount;
        require(_index < assetCount, "Asset index out of range");

        uint24 lastElementIndex = assetCount - 1;

        // copy last element to index of element to be removed
        projects[_projectId].externalAssetDependencies[_index] = projects[
            _projectId
        ].externalAssetDependencies[lastElementIndex];

        delete projects[_projectId].externalAssetDependencies[lastElementIndex];

        projects[_projectId].externalAssetDependencyCount = lastElementIndex;

        emit ExternalAssetDependencyRemoved(_projectId, _index);
    }

    /**
     * @notice Adds external asset dependency for project `_projectId`.
     * @param _projectId Project to be updated.
     * @param _cidOrData Asset cid (Content identifier) or data string to be translated into bytecode.
     * @param _dependencyType Asset dependency type.
     *  0 - IPFS
     *  1 - ARWEAVE
     *  2 - ONCHAIN
     */
    function addProjectExternalAssetDependency(
        uint256 _projectId,
        string memory _cidOrData,
        ExternalAssetDependencyType _dependencyType
    ) external {
        _onlyUnlockedProjectExternalAssetDependencies(_projectId);
        _onlyArtistOrAdminACL(
            _projectId,
            this.addProjectExternalAssetDependency.selector
        );
        uint24 assetCount = projects[_projectId].externalAssetDependencyCount;
        address _bytecodeAddress = address(0);
        // if the incoming dependency type is onchain, we need to write the data to bytecode
        if (_dependencyType == ExternalAssetDependencyType.ONCHAIN) {
            _bytecodeAddress = _cidOrData.writeToBytecode();
            // we don't want to emit data, so we emit the cid as an empty string
            _cidOrData = "";
        }
        ExternalAssetDependency memory asset = ExternalAssetDependency({
            cid: _cidOrData,
            dependencyType: _dependencyType,
            bytecodeAddress: _bytecodeAddress
        });
        projects[_projectId].externalAssetDependencies[assetCount] = asset;
        projects[_projectId].externalAssetDependencyCount = assetCount + 1;

        emit ExternalAssetDependencyUpdated(
            _projectId,
            assetCount,
            _cidOrData,
            _dependencyType,
            assetCount + 1
        );
    }

    /**
     * @notice Mints a token from project `_projectId` and sets the
     * token's owner to `_to`. Hash may or may not be assigned to the token
     * during the mint transaction, depending on the randomizer contract.
     * @param _to Address to be the minted token's owner.
     * @param _projectId Project ID to mint a token on.
     * @param _by Purchaser of minted token.
     * @return _tokenId The ID of the minted token.
     * @dev sender must be the allowed minterContract
     * @dev name of function is optimized for gas usage
     */
    function mint_Ecf(
        address _to,
        uint256 _projectId,
        address _by
    ) external returns (uint256 _tokenId) {
        // CHECKS
        require(msg.sender == minterContract, "Must mint from minter contract");
        Project storage project = projects[_projectId];
        // load invocations into memory
        uint24 invocationsBefore = project.invocations;
        uint24 invocationsAfter;
        unchecked {
            // invocationsBefore guaranteed <= maxInvocations <= 1_000_000,
            // 1_000_000 << max uint24, so no possible overflow
            invocationsAfter = invocationsBefore + 1;
        }
        uint24 maxInvocations = project.maxInvocations;

        require(
            invocationsBefore < maxInvocations,
            "Must not exceed max invocations"
        );
        require(
            project.active ||
                _by == projectIdToFinancials[_projectId].artistAddress,
            "Project must exist and be active"
        );
        require(
            !project.paused ||
                _by == projectIdToFinancials[_projectId].artistAddress,
            "Purchases are paused."
        );

        // EFFECTS
        // increment project's invocations
        project.invocations = invocationsAfter;
        uint256 thisTokenId;
        unchecked {
            // invocationsBefore is uint24 << max uint256. In production use,
            // _projectId * ONE_MILLION must be << max uint256, otherwise
            // tokenIdToProjectId function become invalid.
            // Therefore, no risk of overflow
            thisTokenId = (_projectId * ONE_MILLION) + invocationsBefore;
        }

        // mark project as completed if hit max invocations
        if (invocationsAfter == maxInvocations) {
            _completeProject(_projectId);
        }

        // INTERACTIONS
        _mint(_to, thisTokenId);

        // token hash is updated by the randomizer contract on V3
        randomizerContract.assignTokenHash(thisTokenId);

        // Do not need to also log `projectId` in event, as the `projectId` for
        // a given token can be derived from the `tokenId` with:
        //   projectId = tokenId / 1_000_000
        emit Mint(_to, thisTokenId);

        return thisTokenId;
    }

    /**
     * @notice Sets the hash seed for a given token ID `_tokenId`.
     * May only be called by the current randomizer contract.
     * May only be called for tokens that have not already been assigned a
     * non-zero hash.
     * @param _tokenId Token ID to set the hash for.
     * @param _hashSeed Hash seed to set for the token ID. Only last 12 bytes
     * will be used.
     * @dev gas-optimized function name because called during mint sequence
     * @dev if a separate event is required when the token hash is set, e.g.
     * for indexing purposes, it must be emitted by the randomizer. This is to
     * minimize gas when minting.
     */
    function setTokenHash_8PT(uint256 _tokenId, bytes32 _hashSeed) external {
        _onlyValidTokenId(_tokenId);

        OwnerAndHashSeed storage ownerAndHashSeed = _ownersAndHashSeeds[
            _tokenId
        ];
        require(
            msg.sender == address(randomizerContract),
            "Only randomizer may set"
        );
        require(
            ownerAndHashSeed.hashSeed == bytes12(0),
            "Token hash already set"
        );
        require(_hashSeed != bytes12(0), "No zero hash seed");
        ownerAndHashSeed.hashSeed = bytes12(_hashSeed);
    }

    /**
     * @notice Allows owner (AdminACL) to revoke ownership of the contract.
     * Note that the contract is intended to continue to function after the
     * owner renounces ownership, but no new projects will be able to be added.
     * Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the
     * owner/AdminACL contract. The same is true for any dependent contracts
     * that also integrate with the owner/AdminACL contract (e.g. potentially
     * minter suite contracts, registry contracts, etc.).
     * After renouncing ownership, artists will be in control of updates to
     * their payment addresses and splits (see modifier
     * onlyAdminACLOrRenouncedArtist`).
     * While there is no currently intended reason to call this method based on
     * typical Engine partner business practices, this method exists to allow
     * artists to continue to maintain the limited set of contract
     * functionality that exists post-project-lock in an environment in which
     * there is no longer an admin maintaining this smart contract.
     * @dev This function is intended to be called directly by the AdminACL,
     * not by an address allowed by the AdminACL contract.
     */
    function renounceOwnership() public override onlyOwner {
        // broadcast that new projects are no longer allowed (if not already)
        _forbidNewProjects();
        // renounce ownership viw Ownable
        Ownable.renounceOwnership();
    }

    /**
     * @notice Updates reference to Art Blocks Dependency Registry contract.
     * @param _artblocksDependencyRegistryAddress Address of new Dependency
     * Registry.
     */
    function updateArtblocksDependencyRegistryAddress(
        address _artblocksDependencyRegistryAddress
    ) external {
        _onlyAdminACL(this.updateArtblocksDependencyRegistryAddress.selector);
        _onlyNonZeroAddress(_artblocksDependencyRegistryAddress);
        artblocksDependencyRegistryAddress = _artblocksDependencyRegistryAddress;
        emit PlatformUpdated(FIELD_ARTBLOCKS_DEPENDENCY_REGISTRY_ADDRESS);
    }

    /**
     * @notice Updates sales addresses for the platform and render providers to
     * the input parameters.
     * @param _renderProviderPrimarySalesAddress Address of new primary sales
     * payment address.
     * @param _renderProviderSecondarySalesAddress Address of new secondary sales
     * payment address.
     * @param _platformProviderPrimarySalesAddress Address of new primary sales
     * payment address.
     * @param _platformProviderSecondarySalesAddress Address of new secondary sales
     * payment address.
     */
    function updateProviderSalesAddresses(
        address payable _renderProviderPrimarySalesAddress,
        address payable _renderProviderSecondarySalesAddress,
        address payable _platformProviderPrimarySalesAddress,
        address payable _platformProviderSecondarySalesAddress
    ) external {
        _onlyAdminACL(this.updateProviderSalesAddresses.selector);
        _onlyNonZeroAddress(_renderProviderPrimarySalesAddress);
        _onlyNonZeroAddress(_renderProviderSecondarySalesAddress);
        _onlyNonZeroAddress(_platformProviderPrimarySalesAddress);
        _onlyNonZeroAddress(_platformProviderSecondarySalesAddress);
        _updateProviderSalesAddresses(
            _renderProviderPrimarySalesAddress,
            _renderProviderSecondarySalesAddress,
            _platformProviderPrimarySalesAddress,
            _platformProviderSecondarySalesAddress
        );
    }

    /**
     * @notice Updates the render and platform provider primary sales revenue percentage to
     * the provided inputs.
     * @param renderProviderPrimarySalesPercentage_ New primary sales revenue % for the render provider
     * @param platformProviderPrimarySalesPercentage_ New primary sales revenue % for the platform provider
     * percentage.
     */
    function updateProviderPrimarySalesPercentages(
        uint256 renderProviderPrimarySalesPercentage_,
        uint256 platformProviderPrimarySalesPercentage_
    ) external {
        _onlyAdminACL(this.updateProviderPrimarySalesPercentages.selector);

        // Validate that the sum of the proposed %s, does not exceed 100%.
        require(
            (renderProviderPrimarySalesPercentage_ +
                platformProviderPrimarySalesPercentage_) <= ONE_HUNDRED,
            "Max sum of ONE_HUNDRED %"
        );
        // Casting to `uint8` here is safe due check above, which does not allow
        // overflow as of solidity version ^0.8.0.
        _renderProviderPrimarySalesPercentage = uint8(
            renderProviderPrimarySalesPercentage_
        );
        _platformProviderPrimarySalesPercentage = uint8(
            platformProviderPrimarySalesPercentage_
        );
        emit PlatformUpdated(FIELD_PROVIDER_PRIMARY_SALES_PERCENTAGES);
    }

    /**
     * @notice Updates render and platform provider secondary sales royalty Basis Points to
     * the provided inputs.
     * @param _renderProviderSecondarySalesBPS New secondary sales royalty Basis
     * points.
     * @param _platformProviderSecondarySalesBPS New secondary sales royalty Basis
     * points.
     * @dev Due to secondary royalties being ultimately enforced via social
     * consensus, no hard upper limit is imposed on the BPS value, other than
     * <= 100% royalty, which would not make mathematical sense. Realistically,
     * changing this value is expected to either never occur, or be a rare
     * occurrence.
     */
    function updateProviderSecondarySalesBPS(
        uint256 _renderProviderSecondarySalesBPS,
        uint256 _platformProviderSecondarySalesBPS
    ) external {
        _onlyAdminACL(this.updateProviderSecondarySalesBPS.selector);
        // Validate that the sum of the proposed provider BPS, does not exceed 10_000 BPS.
        require(
            (_renderProviderSecondarySalesBPS +
                _platformProviderSecondarySalesBPS) <=
                MAX_PROVIDER_SECONDARY_SALES_BPS,
            "Over max sum of BPS"
        );
        renderProviderSecondarySalesBPS = _renderProviderSecondarySalesBPS;
        platformProviderSecondarySalesBPS = _platformProviderSecondarySalesBPS;
        emit PlatformUpdated(FIELD_PROVIDER_SECONDARY_SALES_BPS);
    }

    /**
     * @notice Updates minter to `_address`.
     * @param _address Address of new minter.
     */
    function updateMinterContract(address _address) external {
        _onlyAdminACL(this.updateMinterContract.selector);
        _onlyNonZeroAddress(_address);
        minterContract = _address;
        emit MinterUpdated(_address);
    }

    /**
     * @notice Updates randomizer to `_randomizerAddress`.
     * @param _randomizerAddress Address of new randomizer.
     */
    function updateRandomizerAddress(address _randomizerAddress) external {
        _onlyAdminACL(this.updateRandomizerAddress.selector);
        _onlyNonZeroAddress(_randomizerAddress);
        _updateRandomizerAddress(_randomizerAddress);
    }

    /**
     * @notice Toggles project `_projectId` as active/inactive.
     * @param _projectId Project ID to be toggled.
     */
    function toggleProjectIsActive(uint256 _projectId) external {
        _onlyAdminACL(this.toggleProjectIsActive.selector);
        _onlyValidProjectId(_projectId);
        projects[_projectId].active = !projects[_projectId].active;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_ACTIVE);
    }

    /**
     * @notice Artist proposes updated set of artist address, additional payee
     * addresses, and percentage splits for project `_projectId`. Addresses and
     * percentages do not have to all be changed, but they must all be defined
     * as a complete set.
     * Note that if the artist is only proposing a change to the payee percentage
     * splits, without modifying the payee addresses, the proposal will be
     * automatically approved and the new splits will become active immediately.
     * Automatic approval will also be granted if the artist is only removing
     * additional payee addresses, without adding any new ones.
     * Also note that if `autoApproveArtistSplitProposals` is true, proposals
     * will always be auto-approved, regardless of what is being changed.
     * Also note that if the artist is proposing sending funds to the zero
     * address, this function will revert and the proposal will not be created.
     * @param _projectId Project ID.
     * @param _artistAddress Artist address that controls the project, and may
     * receive payments.
     * @param _additionalPayeePrimarySales Address that may receive a
     * percentage split of the artist's primary sales revenue.
     * @param _additionalPayeePrimarySalesPercentage Percent of artist's
     * portion of primary sale revenue that will be split to address
     * `_additionalPayeePrimarySales`.
     * @param _additionalPayeeSecondarySales Address that may receive a percentage
     * split of the secondary sales royalties.
     * @param _additionalPayeeSecondarySalesPercentage Percent of artist's portion
     * of secondary sale royalties that will be split to address
     * `_additionalPayeeSecondarySales`.
     * @dev `_artistAddress` must be a valid address (non-zero-address), but it
     * is intentionally allowable for `_additionalPayee{Primary,Secondaary}Sales`
     * and their associated percentages to be zero'd out by the controlling artist.
     */
    function proposeArtistPaymentAddressesAndSplits(
        uint256 _projectId,
        address payable _artistAddress,
        address payable _additionalPayeePrimarySales,
        uint256 _additionalPayeePrimarySalesPercentage,
        address payable _additionalPayeeSecondarySales,
        uint256 _additionalPayeeSecondarySalesPercentage
    ) external {
        _onlyValidProjectId(_projectId);
        _onlyArtist(_projectId);
        _onlyNonZeroAddress(_artistAddress);
        ProjectFinance storage projectFinance = projectIdToFinancials[
            _projectId
        ];
        // checks
        require(
            _additionalPayeePrimarySalesPercentage <= ONE_HUNDRED &&
                _additionalPayeeSecondarySalesPercentage <= ONE_HUNDRED,
            "Max of 100%"
        );
        require(
            _additionalPayeePrimarySalesPercentage == 0 ||
                _additionalPayeePrimarySales != address(0),
            "Primary payee is zero address"
        );
        require(
            _additionalPayeeSecondarySalesPercentage == 0 ||
                _additionalPayeeSecondarySales != address(0),
            "Secondary payee is zero address"
        );
        // effects
        // emit event for off-chain indexing
        // note: always emit a proposal event, even in the pathway of
        // automatic approval, to simplify indexing expectations
        emit ProposedArtistAddressesAndSplits(
            _projectId,
            _artistAddress,
            _additionalPayeePrimarySales,
            _additionalPayeePrimarySalesPercentage,
            _additionalPayeeSecondarySales,
            _additionalPayeeSecondarySalesPercentage
        );
        // automatically accept if no proposed addresses modifications, or if
        // the proposal only removes payee addresses, or if contract is set to
        // always auto-approve.
        // store proposal hash on-chain, only if not automatic accept
        bool automaticAccept = autoApproveArtistSplitProposals;
        if (!automaticAccept) {
            // block scope to avoid stack too deep error
            bool artistUnchanged = _artistAddress ==
                projectFinance.artistAddress;
            bool additionalPrimaryUnchangedOrRemoved = (_additionalPayeePrimarySales ==
                    projectFinance.additionalPayeePrimarySales) ||
                    (_additionalPayeePrimarySales == address(0));
            bool additionalSecondaryUnchangedOrRemoved = (_additionalPayeeSecondarySales ==
                    projectFinance.additionalPayeeSecondarySales) ||
                    (_additionalPayeeSecondarySales == address(0));
            automaticAccept =
                artistUnchanged &&
                additionalPrimaryUnchangedOrRemoved &&
                additionalSecondaryUnchangedOrRemoved;
        }
        if (automaticAccept) {
            // clear any previously proposed values
            proposedArtistAddressesAndSplitsHash[_projectId] = bytes32(0);
            // update storage
            // artist address can change during automatic accept if
            // autoApproveArtistSplitProposals is true
            projectFinance.artistAddress = _artistAddress;
            projectFinance
                .additionalPayeePrimarySales = _additionalPayeePrimarySales;
            // safe to cast as uint8 as max is 100%, max uint8 is 255
            projectFinance.additionalPayeePrimarySalesPercentage = uint8(
                _additionalPayeePrimarySalesPercentage
            );
            projectFinance
                .additionalPayeeSecondarySales = _additionalPayeeSecondarySales;
            // safe to cast as uint8 as max is 100%, max uint8 is 255
            projectFinance.additionalPayeeSecondarySalesPercentage = uint8(
                _additionalPayeeSecondarySalesPercentage
            );
            // emit event for off-chain indexing
            emit AcceptedArtistAddressesAndSplits(_projectId);
        } else {
            proposedArtistAddressesAndSplitsHash[_projectId] = keccak256(
                abi.encode(
                    _artistAddress,
                    _additionalPayeePrimarySales,
                    _additionalPayeePrimarySalesPercentage,
                    _additionalPayeeSecondarySales,
                    _additionalPayeeSecondarySalesPercentage
                )
            );
        }
    }

    /**
     * @notice Admin accepts a proposed set of updated artist address,
     * additional payee addresses, and percentage splits for project
     * `_projectId`. Addresses and percentages do not have to all be changed,
     * but they must all be defined as a complete set.
     * @param _projectId Project ID.
     * @param _artistAddress Artist address that controls the project, and may
     * receive payments.
     * @param _additionalPayeePrimarySales Address that may receive a
     * percentage split of the artist's primary sales revenue.
     * @param _additionalPayeePrimarySalesPercentage Percent of artist's
     * portion of primary sale revenue that will be split to address
     * `_additionalPayeePrimarySales`.
     * @param _additionalPayeeSecondarySales Address that may receive a percentage
     * split of the secondary sales royalties.
     * @param _additionalPayeeSecondarySalesPercentage Percent of artist's portion
     * of secondary sale royalties that will be split to address
     * `_additionalPayeeSecondarySales`.
     * @dev this must be called by the Admin ACL contract, and must only accept
     * the most recent proposed values for a given project (validated on-chain
     * by comparing the hash of the proposed and accepted values).
     * @dev `_artistAddress` must be a valid address (non-zero-address), but it
     * is intentionally allowable for `_additionalPayee{Primary,Secondaary}Sales`
     * and their associated percentages to be zero'd out by the controlling artist.
     */
    function adminAcceptArtistAddressesAndSplits(
        uint256 _projectId,
        address payable _artistAddress,
        address payable _additionalPayeePrimarySales,
        uint256 _additionalPayeePrimarySalesPercentage,
        address payable _additionalPayeeSecondarySales,
        uint256 _additionalPayeeSecondarySalesPercentage
    ) external {
        _onlyValidProjectId(_projectId);
        _onlyAdminACLOrRenouncedArtist(
            _projectId,
            this.adminAcceptArtistAddressesAndSplits.selector
        );
        _onlyNonZeroAddress(_artistAddress);
        // checks
        require(
            proposedArtistAddressesAndSplitsHash[_projectId] ==
                keccak256(
                    abi.encode(
                        _artistAddress,
                        _additionalPayeePrimarySales,
                        _additionalPayeePrimarySalesPercentage,
                        _additionalPayeeSecondarySales,
                        _additionalPayeeSecondarySalesPercentage
                    )
                ),
            "Must match artist proposal"
        );
        // effects
        ProjectFinance storage projectFinance = projectIdToFinancials[
            _projectId
        ];
        projectFinance.artistAddress = _artistAddress;
        projectFinance
            .additionalPayeePrimarySales = _additionalPayeePrimarySales;
        projectFinance.additionalPayeePrimarySalesPercentage = uint8(
            _additionalPayeePrimarySalesPercentage
        );
        projectFinance
            .additionalPayeeSecondarySales = _additionalPayeeSecondarySales;
        projectFinance.additionalPayeeSecondarySalesPercentage = uint8(
            _additionalPayeeSecondarySalesPercentage
        );
        // clear proposed values
        proposedArtistAddressesAndSplitsHash[_projectId] = bytes32(0);
        // emit event for off-chain indexing
        emit AcceptedArtistAddressesAndSplits(_projectId);
    }

    /**
     * @notice Updates artist of project `_projectId` to `_artistAddress`.
     * This is to only be used in the event that the artist address is
     * compromised or sanctioned.
     * @param _projectId Project ID.
     * @param _artistAddress New artist address.
     */
    function updateProjectArtistAddress(
        uint256 _projectId,
        address payable _artistAddress
    ) external {
        _onlyValidProjectId(_projectId);
        _onlyAdminACLOrRenouncedArtist(
            _projectId,
            this.updateProjectArtistAddress.selector
        );
        _onlyNonZeroAddress(_artistAddress);
        projectIdToFinancials[_projectId].artistAddress = _artistAddress;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_ARTIST_ADDRESS);
    }

    /**
     * @notice Toggles paused state of project `_projectId`.
     * @param _projectId Project ID to be toggled.
     */
    function toggleProjectIsPaused(uint256 _projectId) external {
        _onlyArtist(_projectId);
        projects[_projectId].paused = !projects[_projectId].paused;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_PAUSED);
    }

    /**
     * @notice Adds new project `_projectName` by `_artistAddress`.
     * @param _projectName Project name.
     * @param _artistAddress Artist's address.
     * @dev token price now stored on minter
     */
    function addProject(
        string memory _projectName,
        address payable _artistAddress
    ) external {
        _onlyAdminACL(this.addProject.selector);
        _onlyNonEmptyString(_projectName);
        _onlyNonZeroAddress(_artistAddress);
        require(!newProjectsForbidden, "New projects forbidden");
        uint256 projectId = _nextProjectId;
        projectIdToFinancials[projectId].artistAddress = _artistAddress;
        projects[projectId].name = _projectName;
        projects[projectId].paused = true;
        projects[projectId].maxInvocations = ONE_MILLION_UINT24;
        projects[projectId].projectBaseURI = defaultBaseURI;

        _nextProjectId = uint248(projectId) + 1;
        emit ProjectUpdated(projectId, FIELD_PROJECT_CREATED);
    }

    /**
     * @notice Forever forbids new projects from being added to this contract.
     */
    function forbidNewProjects() external {
        _onlyAdminACL(this.forbidNewProjects.selector);
        require(!newProjectsForbidden, "Already forbidden");
        _forbidNewProjects();
    }

    /**
     * @notice Updates name of project `_projectId` to be `_projectName`.
     * @param _projectId Project ID.
     * @param _projectName New project name.
     */
    function updateProjectName(
        uint256 _projectId,
        string memory _projectName
    ) external {
        _onlyUnlocked(_projectId);
        _onlyArtistOrAdminACL(_projectId, this.updateProjectName.selector);
        _onlyNonEmptyString(_projectName);
        projects[_projectId].name = _projectName;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_NAME);
    }

    /**
     * @notice Updates artist name for project `_projectId` to be
     * `_projectArtistName`.
     * @param _projectId Project ID.
     * @param _projectArtistName New artist name.
     */
    function updateProjectArtistName(
        uint256 _projectId,
        string memory _projectArtistName
    ) external {
        _onlyUnlocked(_projectId);
        _onlyArtistOrAdminACL(
            _projectId,
            this.updateProjectArtistName.selector
        );
        _onlyNonEmptyString(_projectArtistName);
        projects[_projectId].artist = _projectArtistName;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_ARTIST_NAME);
    }

    /**
     * @notice Updates artist secondary market royalties for project
     * `_projectId` to be `_secondMarketRoyalty` percent.
     * This DOES NOT include the secondary market royalty percentages collected
     * by the issuing platform; it is only the total percentage of royalties
     * that will be split to artist and additionalSecondaryPayee.
     * @param _projectId Project ID.
     * @param _secondMarketRoyalty Percent of secondary sales revenue that will
     * be split to artist and additionalSecondaryPayee. This must be less than
     * or equal to ARTIST_MAX_SECONDARY_ROYALTY_PERCENTAGE percent.
     */
    function updateProjectSecondaryMarketRoyaltyPercentage(
        uint256 _projectId,
        uint256 _secondMarketRoyalty
    ) external {
        _onlyArtist(_projectId);
        require(
            _secondMarketRoyalty <= ARTIST_MAX_SECONDARY_ROYALTY_PERCENTAGE,
            "Over max percent"
        );
        projectIdToFinancials[_projectId]
            .secondaryMarketRoyaltyPercentage = uint8(_secondMarketRoyalty);
        emit ProjectUpdated(
            _projectId,
            FIELD_PROJECT_SECONDARY_MARKET_ROYALTY_PERCENTAGE
        );
    }

    /**
     * @notice Updates description of project `_projectId`.
     * Only artist may call when unlocked, only admin may call when locked.
     * Note: The BytecodeStorage library is used to store the description to
     * reduce initial upload cost, however, even minor edits will require an
     * expensive, entirely new bytecode storage contract to be deployed instead
     * of relatively cheap updates to already-warm storage slots. This results
     * in an increased gas cost for minor edits to the description after the
     * initial upload, but an overall decrease in gas cost for projects with
     * less than ~3-5 edits (depending on the length of the description).
     * @param _projectId Project ID.
     * @param _projectDescription New project description.
     */
    function updateProjectDescription(
        uint256 _projectId,
        string memory _projectDescription
    ) external {
        // checks
        require(
            _projectUnlocked(_projectId)
                ? msg.sender == projectIdToFinancials[_projectId].artistAddress
                : adminACLAllowed(
                    msg.sender,
                    address(this),
                    this.updateProjectDescription.selector
                ),
            "Only artist when unlocked, owner when locked"
        );
        // effects
        // store description in contract bytecode, replacing reference address from
        // the old storage description with the newly created one
        projects[_projectId].descriptionAddress = _projectDescription
            .writeToBytecode();
        emit ProjectUpdated(_projectId, FIELD_PROJECT_DESCRIPTION);
    }

    /**
     * @notice Updates website of project `_projectId` to be `_projectWebsite`.
     * @param _projectId Project ID.
     * @param _projectWebsite New project website.
     * @dev It is intentionally allowed for this to be set to the empty string.
     */
    function updateProjectWebsite(
        uint256 _projectId,
        string memory _projectWebsite
    ) external {
        _onlyArtist(_projectId);
        projects[_projectId].website = _projectWebsite;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_WEBSITE);
    }

    /**
     * @notice Updates license for project `_projectId`.
     * @param _projectId Project ID.
     * @param _projectLicense New project license.
     */
    function updateProjectLicense(
        uint256 _projectId,
        string memory _projectLicense
    ) external {
        _onlyUnlocked(_projectId);
        _onlyArtistOrAdminACL(_projectId, this.updateProjectLicense.selector);
        _onlyNonEmptyString(_projectLicense);
        projects[_projectId].license = _projectLicense;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_LICENSE);
    }

    /**
     * @notice Updates maximum invocations for project `_projectId` to
     * `_maxInvocations`. Maximum invocations may only be decreased by the
     * artist, and must be greater than or equal to current invocations.
     * New projects are created with maximum invocations of 1 million by
     * default.
     * @param _projectId Project ID.
     * @param _maxInvocations New maximum invocations.
     */
    function updateProjectMaxInvocations(
        uint256 _projectId,
        uint24 _maxInvocations
    ) external {
        _onlyArtist(_projectId);
        // CHECKS
        Project storage project = projects[_projectId];
        uint256 _invocations = project.invocations;
        require(
            (_maxInvocations < project.maxInvocations),
            "Only maxInvocations decrease"
        );
        require(_maxInvocations >= _invocations, "Only gte invocations");
        // EFFECTS
        project.maxInvocations = _maxInvocations;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_MAX_INVOCATIONS);

        // register completed timestamp if action completed the project
        if (_maxInvocations == _invocations) {
            _completeProject(_projectId);
        }
    }

    /**
     * @notice Adds a script to project `_projectId`.
     * @param _projectId Project to be updated.
     * @param _script Script to be added. Required to be a non-empty string,
     * but no further validation is performed.
     */
    function addProjectScript(
        uint256 _projectId,
        string memory _script
    ) external {
        _onlyUnlocked(_projectId);
        _onlyArtistOrAdminACL(_projectId, this.addProjectScript.selector);
        _onlyNonEmptyString(_script);
        Project storage project = projects[_projectId];
        // store script in contract bytecode
        project.scriptBytecodeAddresses[project.scriptCount] = _script
            .writeToBytecode();
        project.scriptCount = project.scriptCount + 1;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_SCRIPT);
    }

    /**
     * @notice Updates script for project `_projectId` at script ID `_scriptId`.
     * @param _projectId Project to be updated.
     * @param _scriptId Script ID to be updated.
     * @param _script The updated script value. Required to be a non-empty
     *                string, but no further validation is performed.
     */
    function updateProjectScript(
        uint256 _projectId,
        uint256 _scriptId,
        string memory _script
    ) external {
        _onlyUnlocked(_projectId);
        _onlyArtistOrAdminACL(_projectId, this.updateProjectScript.selector);
        _onlyNonEmptyString(_script);
        Project storage project = projects[_projectId];
        require(_scriptId < project.scriptCount, "scriptId out of range");
        // store script in contract bytecode, replacing reference address from
        // the old storage contract with the newly created one
        project.scriptBytecodeAddresses[_scriptId] = _script.writeToBytecode();
        emit ProjectUpdated(_projectId, FIELD_PROJECT_SCRIPT);
    }

    /**
     * @notice Removes last script from project `_projectId`.
     * @param _projectId Project to be updated.
     */
    function removeProjectLastScript(uint256 _projectId) external {
        _onlyUnlocked(_projectId);
        _onlyArtistOrAdminACL(
            _projectId,
            this.removeProjectLastScript.selector
        );
        Project storage project = projects[_projectId];
        require(project.scriptCount > 0, "No scripts to remove");
        // delete reference to old storage contract address
        delete project.scriptBytecodeAddresses[project.scriptCount - 1];
        unchecked {
            project.scriptCount = project.scriptCount - 1;
        }
        emit ProjectUpdated(_projectId, FIELD_PROJECT_SCRIPT);
    }

    /**
     * @notice Updates script type for project `_projectId`.
     * @param _projectId Project to be updated.
     * @param _scriptTypeAndVersion Script type and version e.g. "[email protected]",
     * as bytes32 encoded string.
     */
    function updateProjectScriptType(
        uint256 _projectId,
        bytes32 _scriptTypeAndVersion
    ) external {
        _onlyUnlocked(_projectId);
        _onlyArtistOrAdminACL(
            _projectId,
            this.updateProjectScriptType.selector
        );
        Project storage project = projects[_projectId];
        // require exactly one @ symbol in _scriptTypeAndVersion
        require(
            _scriptTypeAndVersion.containsExactCharacterQty(
                AT_CHARACTER_CODE,
                uint8(1)
            ),
            "must contain exactly one @"
        );
        project.scriptTypeAndVersion = _scriptTypeAndVersion;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_SCRIPT_TYPE);
    }

    /**
     * @notice Updates project's aspect ratio.
     * @param _projectId Project to be updated.
     * @param _aspectRatio Aspect ratio to be set. Intended to be string in the
     * format of a decimal, e.g. "1" for square, "1.77777778" for 16:9, etc.,
     * allowing for a maximum of 10 digits and one (optional) decimal separator.
     */
    function updateProjectAspectRatio(
        uint256 _projectId,
        string memory _aspectRatio
    ) external {
        _onlyUnlocked(_projectId);
        _onlyArtistOrAdminACL(
            _projectId,
            this.updateProjectAspectRatio.selector
        );
        _onlyNonEmptyString(_aspectRatio);
        // Perform more detailed input validation for aspect ratio.
        bytes memory aspectRatioBytes = bytes(_aspectRatio);
        uint256 bytesLength = aspectRatioBytes.length;
        require(bytesLength <= 11, "Aspect ratio format too long");
        bool hasSeenDecimalSeparator = false;
        bool hasSeenNumber = false;
        for (uint256 i; i < bytesLength; i++) {
            bytes1 character = aspectRatioBytes[i];
            // Allow as many #s as desired.
            if (character >= 0x30 && character <= 0x39) {
                // 9-0
                // We need to ensure there is at least 1 `9-0` occurrence.
                hasSeenNumber = true;
                continue;
            }
            if (character == 0x2E) {
                // .
                // Allow no more than 1 `.` occurrence.
                if (!hasSeenDecimalSeparator) {
                    hasSeenDecimalSeparator = true;
                    continue;
                }
            }
            revert("Improperly formatted aspect ratio");
        }
        require(hasSeenNumber, "Aspect ratio has no numbers");

        projects[_projectId].aspectRatio = _aspectRatio;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_ASPECT_RATIO);
    }

    /**
     * @notice Updates base URI for project `_projectId` to `_newBaseURI`.
     * This is the controlling base URI for all tokens in the project. The
     * contract-level defaultBaseURI is only used when initializing new
     * projects.
     * @param _projectId Project to be updated.
     * @param _newBaseURI New base URI.
     */
    function updateProjectBaseURI(
        uint256 _projectId,
        string memory _newBaseURI
    ) external {
        _onlyArtist(_projectId);
        _onlyNonEmptyString(_newBaseURI);
        projects[_projectId].projectBaseURI = _newBaseURI;
        emit ProjectUpdated(_projectId, FIELD_PROJECT_BASE_URI);
    }

    /**
     * @notice Updates default base URI to `_defaultBaseURI`. The
     * contract-level defaultBaseURI is only used when initializing new
     * projects. Token URIs are determined by their project's `projectBaseURI`.
     * @param _defaultBaseURI New default base URI.
     */
    function updateDefaultBaseURI(string memory _defaultBaseURI) external {
        _onlyAdminACL(this.updateDefaultBaseURI.selector);
        _onlyNonEmptyString(_defaultBaseURI);
        _updateDefaultBaseURI(_defaultBaseURI);
    }

    /**
     * @notice Next project ID to be created on this contract.
     * @return uint256 Next project ID.
     */
    function nextProjectId() external view returns (uint256) {
        return _nextProjectId;
    }

    /**
     * @notice Returns token hash for token ID `_tokenId`. Returns null if hash
     * has not been set.
     * @param _tokenId Token ID to be queried.
     * @return bytes32 Token hash.
     * @dev token hash is the keccak256 hash of the stored hash seed
     */
    function tokenIdToHash(uint256 _tokenId) external view returns (bytes32) {
        bytes12 _hashSeed = _ownersAndHashSeeds[_tokenId].hashSeed;
        if (_hashSeed == 0) {
            return 0;
        }
        return keccak256(abi.encode(_hashSeed));
    }

    /**
     * @notice Returns token hash **seed** for token ID `_tokenId`. Returns
     * null if hash seed has not been set. The hash seed id the bytes12 value
     * which is hashed to produce the token hash.
     * @param _tokenId Token ID to be queried.
     * @return bytes12 Token hash seed.
     * @dev token hash seed is keccak256 hashed to give the token hash
     */
    function tokenIdToHashSeed(
        uint256 _tokenId
    ) external view returns (bytes12) {
        return _ownersAndHashSeeds[_tokenId].hashSeed;
    }

    /**
     * @notice View function returning the render provider portion of
     * primary sales, in percent.
     * @return uint256 The render provider portion of primary sales,
     * in percent.
     */
    function renderProviderPrimarySalesPercentage()
        external
        view
        returns (uint256)
    {
        return _renderProviderPrimarySalesPercentage;
    }

    /**
     * @notice View function returning the platform provider portion of
     * primary sales, in percent.
     * @return uint256 The platform provider portion of primary sales,
     * in percent.
     */
    function platformProviderPrimarySalesPercentage()
        external
        view
        returns (uint256)
    {
        return _platformProviderPrimarySalesPercentage;
    }

    /**
     * @notice View function returning Artist's address for project
     * `_projectId`.
     * @param _projectId Project ID to be queried.
     * @return address Artist's address.
     */
    function projectIdToArtistAddress(
        uint256 _projectId
    ) external view returns (address payable) {
        return projectIdToFinancials[_projectId].artistAddress;
    }

    /**
     * @notice View function returning Artist's secondary market royalty
     * percentage for project `_projectId`.
     * This does not include render/platform providers portions of secondary
     * market royalties.
     * @param _projectId Project ID to be queried.
     * @return uint256 Artist's secondary market royalty percentage.
     */
    function projectIdToSecondaryMarketRoyaltyPercentage(
        uint256 _projectId
    ) external view returns (uint256) {
        return
            projectIdToFinancials[_projectId].secondaryMarketRoyaltyPercentage;
    }

    /**
     * @notice View function returning Artist's additional payee address for
     * primary sales, for project `_projectId`.
     * @param _projectId Project ID to be queried.
     * @return address Artist's additional payee address for primary sales.
     */
    function projectIdToAdditionalPayeePrimarySales(
        uint256 _projectId
    ) external view returns (address payable) {
        return projectIdToFinancials[_projectId].additionalPayeePrimarySales;
    }

    /**
     * @notice View function returning Artist's additional payee primary sales
     * percentage, for project `_projectId`.
     * @param _projectId Project ID to be queried.
     * @return uint256 Artist's additional payee primary sales percentage.
     */
    function projectIdToAdditionalPayeePrimarySalesPercentage(
        uint256 _projectId
    ) external view returns (uint256) {
        return
            projectIdToFinancials[_projectId]
                .additionalPayeePrimarySalesPercentage;
    }

    /**
     * @notice View function returning Artist's additional payee address for
     * secondary sales, for project `_projectId`.
     * @param _projectId Project ID to be queried.
     * @return address payable Artist's additional payee address for secondary
     * sales.
     */
    function projectIdToAdditionalPayeeSecondarySales(
        uint256 _projectId
    ) external view returns (address payable) {
        return projectIdToFinancials[_projectId].additionalPayeeSecondarySales;
    }

    /**
     * @notice View function returning Artist's additional payee secondary
     * sales percentage, for project `_projectId`.
     * @param _projectId Project ID to be queried.
     * @return uint256 Artist's additional payee secondary sales percentage.
     */
    function projectIdToAdditionalPayeeSecondarySalesPercentage(
        uint256 _projectId
    ) external view returns (uint256) {
        return
            projectIdToFinancials[_projectId]
                .additionalPayeeSecondarySalesPercentage;
    }

    /**
     * @notice Returns project details for project `_projectId`.
     * @param _projectId Project to be queried.
     * @return projectName Name of project
     * @return artist Artist of project
     * @return description Project description
     * @return website Project website
     * @return license Project license
     * @dev this function was named projectDetails prior to V3 core contract.
     */
    function projectDetails(
        uint256 _projectId
    )
        external
        view
        returns (
            string memory projectName,
            string memory artist,
            string memory description,
            string memory website,
            string memory license
        )
    {
        Project storage project = projects[_projectId];
        projectName = project.name;
        artist = project.artist;
        address projectDescriptionBytecodeAddress = project.descriptionAddress;
        if (projectDescriptionBytecodeAddress == address(0)) {
            description = "";
        } else {
            description = _readFromBytecode(projectDescriptionBytecodeAddress);
        }
        website = project.website;
        license = project.license;
    }

    /**
     * @notice Returns project state data for project `_projectId`.
     * @param _projectId Project to be queried
     * @return invocations Current number of invocations
     * @return maxInvocations Maximum allowed invocations
     * @return active Boolean representing if project is currently active
     * @return paused Boolean representing if project is paused
     * @return completedTimestamp zero if project not complete, otherwise
     * timestamp of project completion.
     * @return locked Boolean representing if project is locked
     * @dev price and currency info are located on minter contracts
     */
    function projectStateData(
        uint256 _projectId
    )
        external
        view
        returns (
            uint256 invocations,
            uint256 maxInvocations,
            bool active,
            bool paused,
            uint256 completedTimestamp,
            bool locked
        )
    {
        Project storage project = projects[_projectId];
        invocations = project.invocations;
        maxInvocations = project.maxInvocations;
        active = project.active;
        paused = project.paused;
        completedTimestamp = project.completedTimestamp;
        locked = !_projectUnlocked(_projectId);
    }

    /**
     * @notice Returns artist payment information for project `_projectId`.
     * @param _projectId Project to be queried
     * @return artistAddress Project Artist's address
     * @return additionalPayeePrimarySales Additional payee address for primary
     * sales
     * @return additionalPayeePrimarySalesPercentage Percentage of artist revenue
     * to be sent to the additional payee address for primary sales
     * @return additionalPayeeSecondarySales Additional payee address for secondary
     * sales royalties
     * @return additionalPayeeSecondarySalesPercentage Percentage of artist revenue
     * to be sent to the additional payee address for secondary sales royalties
     * @return secondaryMarketRoyaltyPercentage Royalty percentage to be sent to
     * combination of artist and additional payee. This does not include the
     * platform's percentage of secondary sales royalties, which is defined as
     * the sum of `renderProviderSecondarySalesBPS`
     * and `platformProviderSecondarySalesBPS`.
     */
    function projectArtistPaymentInfo(
        uint256 _projectId
    )
        external
        view
        returns (
            address artistAddress,
            address additionalPayeePrimarySales,
            uint256 additionalPayeePrimarySalesPercentage,
            address additionalPayeeSecondarySales,
            uint256 additionalPayeeSecondarySalesPercentage,
            uint256 secondaryMarketRoyaltyPercentage
        )
    {
        ProjectFinance storage projectFinance = projectIdToFinancials[
            _projectId
        ];
        artistAddress = projectFinance.artistAddress;
        additionalPayeePrimarySales = projectFinance
            .additionalPayeePrimarySales;
        additionalPayeePrimarySalesPercentage = projectFinance
            .additionalPayeePrimarySalesPercentage;
        additionalPayeeSecondarySales = projectFinance
            .additionalPayeeSecondarySales;
        additionalPayeeSecondarySalesPercentage = projectFinance
            .additionalPayeeSecondarySalesPercentage;
        secondaryMarketRoyaltyPercentage = projectFinance
            .secondaryMarketRoyaltyPercentage;
    }

    /**
     * @notice Returns script information for project `_projectId`.
     * @param _projectId Project to be queried.
     * @return scriptTypeAndVersion Project's script type and version
     * (e.g. "p5js(atSymbol)1.0.0")
     * @return aspectRatio Aspect ratio of project (e.g. "1" for square,
     * "1.77777778" for 16:9, etc.)
     * @return scriptCount Count of scripts for project
     */
    function projectScriptDetails(
        uint256 _projectId
    )
        external
        view
        override(IGenArt721CoreContractV3_Base, IDependencyRegistryCompatibleV0)
        returns (
            string memory scriptTypeAndVersion,
            string memory aspectRatio,
            uint256 scriptCount
        )
    {
        Project storage project = projects[_projectId];
        scriptTypeAndVersion = project.scriptTypeAndVersion.toString();
        aspectRatio = project.aspectRatio;
        scriptCount = project.scriptCount;
    }

    /**
     * @notice Returns address with bytecode containing project script for
     * project `_projectId` at script index `_index`.
     */
    function projectScriptBytecodeAddressByIndex(
        uint256 _projectId,
        uint256 _index
    ) external view returns (address) {
        return projects[_projectId].scriptBytecodeAddresses[_index];
    }

    /**
     * @notice Returns script for project `_projectId` at script index `_index`.
     * @param _projectId Project to be queried.
     * @param _index Index of script to be queried.
     */
    function projectScriptByIndex(
        uint256 _projectId,
        uint256 _index
    ) external view returns (string memory) {
        Project storage project = projects[_projectId];
        // If trying to access an out-of-index script, return the empty string.
        if (_index >= project.scriptCount) {
            return "";
        }
        return _readFromBytecode(project.scriptBytecodeAddresses[_index]);
    }

    /**
     * @notice Returns base URI for project `_projectId`.
     * @param _projectId Project to be queried.
     * @return projectBaseURI Base URI for project
     */
    function projectURIInfo(
        uint256 _projectId
    ) external view returns (string memory projectBaseURI) {
        projectBaseURI = projects[_projectId].projectBaseURI;
    }

    /**
     * @notice Backwards-compatible (pre-V3) function returning if `_minter` is
     * minterContract.
     * @param _minter Address to be queried.
     * @return bool Boolean representing if `_minter` is minterContract.
     */
    function isMintWhitelisted(address _minter) external view returns (bool) {
        return (minterContract == _minter);
    }

    /**
     * @notice Gets qty of randomizers in history of all randomizers used by
     * this core contract. If a randomizer is switched away from then back to,
     * it will show up in the history twice.
     * @return randomizerHistoryCount Count of randomizers in history
     */
    function numHistoricalRandomizers() external view returns (uint256) {
        return _historicalRandomizerAddresses.length;
    }

    /**
     * @notice Gets address of randomizer at index `_index` in history of all
     * randomizers used by this core contract. Index is zero-based.
     * @param _index Historical index of randomizer to be queried.
     * @return randomizerAddress Address of randomizer at index `_index`.
     * @dev If a randomizer is switched away from and then switched back to, it
     * will show up in the history twice.
     */
    function getHistoricalRandomizerAt(
        uint256 _index
    ) external view returns (address) {
        require(
            _index < _historicalRandomizerAddresses.length,
            "Index out of bounds"
        );
        return _historicalRandomizerAddresses[_index];
    }

    /**
     * @notice Gets royalty Basis Points (BPS) for token ID `_tokenId`.
     * This conforms to the IManifold interface designated in the Royalty
     * Registry's RoyaltyEngineV1.sol contract.
     * ref: https://github.com/manifoldxyz/royalty-registry-solidity
     * @param _tokenId Token ID to be queried.
     * @return recipients Array of royalty payment recipients
     * @return bps Array of Basis Points (BPS) allocated to each recipient,
     * aligned by index.
     * @dev reverts if invalid _tokenId
     * @dev only returns recipients that have a non-zero BPS allocation
     */
    function getRoyalties(
        uint256 _tokenId
    )
        external
        view
        returns (address payable[] memory recipients, uint256[] memory bps)
    {
        _onlyValidTokenId(_tokenId);
        // initialize arrays with maximum potential length
        recipients = new address payable[](4);
        bps = new uint256[](4);

        uint256 projectId = tokenIdToProjectId(_tokenId);
        ProjectFinance storage projectFinance = projectIdToFinancials[
            projectId
        ];
        // load values into memory
        uint256 royaltyPercentageForArtistAndAdditional = projectFinance
            .secondaryMarketRoyaltyPercentage;
        uint256 additionalPayeePercentage = projectFinance
            .additionalPayeeSecondarySalesPercentage;
        // calculate BPS = percentage * 100
        uint256 artistBPS = (ONE_HUNDRED - additionalPayeePercentage) *
            royaltyPercentageForArtistAndAdditional;

        uint256 additionalBPS = additionalPayeePercentage *
            royaltyPercentageForArtistAndAdditional;
        uint256 renderProviderBPS = renderProviderSecondarySalesBPS;
        uint256 platformProviderBPS = platformProviderSecondarySalesBPS;
        // populate arrays
        uint256 payeeCount;
        if (artistBPS > 0) {
            recipients[payeeCount] = projectFinance.artistAddress;
            bps[payeeCount++] = artistBPS;
        }
        if (additionalBPS > 0) {
            recipients[payeeCount] = projectFinance
                .additionalPayeeSecondarySales;
            bps[payeeCount++] = additionalBPS;
        }
        if (renderProviderBPS > 0) {
            recipients[payeeCount] = renderProviderSecondarySalesAddress;
            bps[payeeCount++] = renderProviderBPS;
        }
        if (platformProviderBPS > 0) {
            recipients[payeeCount] = platformProviderSecondarySalesAddress;
            bps[payeeCount++] = platformProviderBPS;
        }
        // trim arrays if necessary
        if (4 > payeeCount) {
            assembly {
                let decrease := sub(4, payeeCount)
                mstore(recipients, sub(mload(recipients), decrease))
                mstore(bps, sub(mload(bps), decrease))
            }
        }
        return (recipients, bps);
    }

    /**
     * @notice View function that returns appropriate revenue splits between
     * different render provider, platform provider, Artist, and Artist's
     * additional primary sales payee given a sale price of `_price` on
     * project `_projectId`.
     * This always returns four revenue amounts and four addresses, but if a
     * revenue is zero for either Artist or additional payee, the corresponding
     * address returned will also be null (for gas optimization).
     * Does not account for refund if user overpays for a token (minter should
     * handle a refund of the difference, if appropriate).
     * Some minters may have alternative methods of splitting payments, in
     * which case they should implement their own payment splitting logic.
     * @param _projectId Project ID to be queried.
     * @param _price Sale price of token.
     * @return renderProviderRevenue_ amount of revenue to be sent to the
     * render provider
     * @return renderProviderAddress_ address to send render provider revenue to
     * @return platformProviderRevenue_ amount of revenue to be sent to the
     * platform provider
     * @return platformProviderAddress_ address to send platform provider revenue to
     * @return artistRevenue_ amount of revenue to be sent to Artist
     * @return artistAddress_ address to send Artist revenue to. Will be null
     * if no revenue is due to artist (gas optimization).
     * @return additionalPayeePrimaryRevenue_ amount of revenue to be sent to
     * additional payee for primary sales
     * @return additionalPayeePrimaryAddress_ address to send Artist's
     * additional payee for primary sales revenue to. Will be null if no
     * revenue is due to additional payee for primary sales (gas optimization).
     * @dev this always returns four addresses and four revenues, but if the
     * revenue is zero, the corresponding address will be address(0). It is up
     * to the contract performing the revenue split to handle this
     * appropriately.
     */
    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_
        )
    {
        ProjectFinance storage projectFinance = projectIdToFinancials[
            _projectId
        ];
        // calculate revenues – this is a three-way split between the
        // render provider, the platform provider, and the artist, and
        // is safe to perform this given that in the case of loss of
        // precision Solidity will round down.
        uint256 projectFunds = _price;
        renderProviderRevenue_ =
            (_price * uint256(_renderProviderPrimarySalesPercentage)) /
            ONE_HUNDRED;
        // renderProviderRevenue_ percentage is always <=100, so guaranteed to never underflow
        projectFunds -= renderProviderRevenue_;
        platformProviderRevenue_ =
            (_price * uint256(_platformProviderPrimarySalesPercentage)) /
            ONE_HUNDRED;
        // platformProviderRevenue_ percentage is always <=100, so guaranteed to never underflow
        projectFunds -= platformProviderRevenue_;
        additionalPayeePrimaryRevenue_ =
            (projectFunds *
                projectFinance.additionalPayeePrimarySalesPercentage) /
            ONE_HUNDRED;
        // projectIdToAdditionalPayeePrimarySalesPercentage is always
        // <=100, so guaranteed to never underflow
        artistRevenue_ = projectFunds - additionalPayeePrimaryRevenue_;
        // set addresses from storage
        renderProviderAddress_ = renderProviderPrimarySalesAddress;
        platformProviderAddress_ = platformProviderPrimarySalesAddress;
        if (artistRevenue_ > 0) {
            artistAddress_ = projectFinance.artistAddress;
        }
        if (additionalPayeePrimaryRevenue_ > 0) {
            additionalPayeePrimaryAddress_ = projectFinance
                .additionalPayeePrimarySales;
        }
    }

    /**
     * @notice Returns external asset dependency for project `_projectId` at index `_index`.
     * If the dependencyType is ONCHAIN, the `data` field will contain the extrated bytecode data and `cid`
     * will be an empty string. Conversly, for any other dependencyType, the `data` field will be an empty string
     * and the `bytecodeAddress` will point to the zero address.
     */
    function projectExternalAssetDependencyByIndex(
        uint256 _projectId,
        uint256 _index
    ) external view returns (ExternalAssetDependencyWithData memory) {
        ExternalAssetDependency storage _dependency = projects[_projectId]
            .externalAssetDependencies[_index];
        address _bytecodeAddress = _dependency.bytecodeAddress;

        return
            ExternalAssetDependencyWithData({
                dependencyType: _dependency.dependencyType,
                cid: _dependency.cid,
                bytecodeAddress: _bytecodeAddress,
                data: (_dependency.dependencyType ==
                    ExternalAssetDependencyType.ONCHAIN)
                    ? _readFromBytecode(_bytecodeAddress)
                    : ""
            });
    }

    /**
     * @notice Returns external asset dependency count for project `_projectId` at index `_index`.
     */
    function projectExternalAssetDependencyCount(
        uint256 _projectId
    ) external view returns (uint256) {
        return uint256(projects[_projectId].externalAssetDependencyCount);
    }

    /**
     * @notice Backwards-compatible (pre-V3) getter returning contract admin
     * @return address Address of contract admin (same as owner)
     */
    function admin() external view returns (address) {
        return owner();
    }

    /**
     * @notice Gets the project ID for a given `_tokenId`.
     * @param _tokenId Token ID to be queried.
     * @return _projectId Project ID for given `_tokenId`.
     */
    function tokenIdToProjectId(
        uint256 _tokenId
    ) public pure returns (uint256 _projectId) {
        return _tokenId / ONE_MILLION;
    }

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

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

    /**
     * @notice Gets token URI for token ID `_tokenId`.
     * @param _tokenId Token ID to be queried.
     * @return string URI of token ID `_tokenId`.
     * @dev token URIs are the concatenation of the project base URI and the
     * token ID.
     */
    function tokenURI(
        uint256 _tokenId
    ) public view override returns (string memory) {
        _onlyValidTokenId(_tokenId);
        string memory _projectBaseURI = projects[tokenIdToProjectId(_tokenId)]
            .projectBaseURI;
        return string.concat(_projectBaseURI, toString(_tokenId));
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override returns (bool) {
        return
            interfaceId == type(IManifold).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @notice Forbids new projects from being created
     * @dev only performs operation and emits event if contract is not already
     * forbidding new projects.
     */
    function _forbidNewProjects() internal {
        if (!newProjectsForbidden) {
            newProjectsForbidden = true;
            emit PlatformUpdated(FIELD_NEW_PROJECTS_FORBIDDEN);
        }
    }

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

    /**
     * @notice Updates sales addresses for the platform and render providers to
     * the input parameters.
     * @param _renderProviderPrimarySalesAddress Address of new primary sales
     * payment address.
     * @param _renderProviderSecondarySalesAddress Address of new secondary sales
     * payment address.
     * @param _platformProviderPrimarySalesAddress Address of new primary sales
     * payment address.
     * @param _platformProviderSecondarySalesAddress Address of new secondary sales
     * payment address.
     * @dev Note that this method does not check that the input address is
     * not `address(0)`, as it is expected that callers of this method should
     * perform input validation where applicable.
     */
    function _updateProviderSalesAddresses(
        address _renderProviderPrimarySalesAddress,
        address _renderProviderSecondarySalesAddress,
        address _platformProviderPrimarySalesAddress,
        address _platformProviderSecondarySalesAddress
    ) internal {
        platformProviderPrimarySalesAddress = payable(
            _platformProviderPrimarySalesAddress
        );
        platformProviderSecondarySalesAddress = payable(
            _platformProviderSecondarySalesAddress
        );
        renderProviderPrimarySalesAddress = payable(
            _renderProviderPrimarySalesAddress
        );
        renderProviderSecondarySalesAddress = payable(
            _renderProviderSecondarySalesAddress
        );
        emit PlatformUpdated(FIELD_PROVIDER_SALES_ADDRESSES);
    }

    /**
     * @notice Updates randomizer address to `_randomizerAddress`.
     * @param _randomizerAddress New randomizer address.
     * @dev Note that this method does not check that the input address is
     * not `address(0)`, as it is expected that callers of this method should
     * perform input validation where applicable.
     */
    function _updateRandomizerAddress(address _randomizerAddress) internal {
        randomizerContract = IRandomizer_V3CoreBase(_randomizerAddress);
        // populate historical randomizer array
        _historicalRandomizerAddresses.push(_randomizerAddress);
        emit PlatformUpdated(FIELD_RANDOMIZER_ADDRESS);
    }

    /**
     * @notice Updates default base URI to `_defaultBaseURI`.
     * When new projects are added, their `projectBaseURI` is automatically
     * initialized to `_defaultBaseURI`.
     * @param _defaultBaseURI New default base URI.
     * @dev Note that this method does not check that the input string is not
     * the empty string, as it is expected that callers of this method should
     * perform input validation where applicable.
     */
    function _updateDefaultBaseURI(string memory _defaultBaseURI) internal {
        defaultBaseURI = _defaultBaseURI;
        emit PlatformUpdated(FIELD_DEFAULT_BASE_URI);
    }

    /**
     * @notice Internal function to complete a project.
     * @param _projectId Project ID to be completed.
     */
    function _completeProject(uint256 _projectId) internal {
        projects[_projectId].completedTimestamp = uint64(block.timestamp);
        emit ProjectUpdated(_projectId, FIELD_PROJECT_COMPLETED);
    }

    /**
     * @notice Internal function that returns whether a project is unlocked.
     * Projects automatically lock four weeks after they are completed.
     * Projects are considered completed when they have been invoked the
     * maximum number of times.
     * @param _projectId Project ID to be queried.
     * @return bool true if project is unlocked, false otherwise.
     * @dev This also enforces that the `_projectId` passed in is valid.
     */
    function _projectUnlocked(uint256 _projectId) internal view returns (bool) {
        _onlyValidProjectId(_projectId);
        uint256 projectCompletedTimestamp = projects[_projectId]
            .completedTimestamp;
        bool projectOpen = projectCompletedTimestamp == 0;
        return
            projectOpen ||
            (block.timestamp - projectCompletedTimestamp <
                FOUR_WEEKS_IN_SECONDS);
    }

    /**
     * Helper for calling `BytecodeStorageReader` external library reader method,
     * added for bytecode size reduction purposes.
     */
    function _readFromBytecode(
        address _address
    ) internal view returns (string memory) {
        return BytecodeStorageReader.readFromBytecode(_address);
    }

    // strings library from OpenZeppelin, modified for no constants

    bytes16 private _HEX_SYMBOLS = "0123456789abcdef";
    uint8 private _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(
        uint256 value,
        uint256 length
    ) internal view 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] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "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 view returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 2 of 21 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "../IERC721.sol";

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

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

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

File 4 of 21 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

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

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

File 5 of 21 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

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

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 7 of 21 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

File 8 of 21 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

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

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_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) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @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] = _HEX_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 11 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 12 of 21 : IDependencyRegistryCompatibleV0.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.19;

interface IDependencyRegistryCompatibleV0 {
    /// Dependency registry managed by Art Blocks
    function artblocksDependencyRegistryAddress()
        external
        view
        returns (address);

    /**
     * @notice Returns script information for project `_projectId`.
     * @param _projectId Project to be queried.
     * @return scriptTypeAndVersion Project's script type and version
     * (e.g. "p5js(atSymbol)1.0.0")
     * @return aspectRatio Aspect ratio of project (e.g. "1" for square,
     * "1.77777778" for 16:9, etc.)
     * @return scriptCount Count of scripts for project
     */
    function projectScriptDetails(
        uint256 _projectId
    )
        external
        view
        returns (
            string memory scriptTypeAndVersion,
            string memory aspectRatio,
            uint256 scriptCount
        );
}

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

pragma solidity ^0.8.0;

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

File 14 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 startingProjectId() external view returns (uint256);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "./IAdminACLV0.sol";
import "./IGenArt721CoreContractV3_Engine.sol";

/**
 * @title This interface is intended to house interface items that are common
 * across all GenArt721CoreContractV3 Engine Flex and derivative implementations.
 * @author Art Blocks Inc.
 */
interface IGenArt721CoreContractV3_Engine_Flex is
    IGenArt721CoreContractV3_Engine
{
    /**
     * @notice When an external asset dependency is updated or added, this event is emitted.
     * @param _projectId The project ID of the project that was updated.
     * @param _index The index of the external asset dependency that was updated.
     * @param _cid The content ID of the external asset dependency. This is an empty string
     * if the dependency type is ONCHAIN.
     * @param _dependencyType The type of the external asset dependency.
     * @param _externalAssetDependencyCount The number of external asset dependencies.
     */
    event ExternalAssetDependencyUpdated(
        uint256 indexed _projectId,
        uint256 indexed _index,
        string _cid,
        ExternalAssetDependencyType _dependencyType,
        uint24 _externalAssetDependencyCount
    );

    /**
     * @notice The project id `_projectId` has had an external asset dependency removed at index `_index`.
     */
    event ExternalAssetDependencyRemoved(
        uint256 indexed _projectId,
        uint256 indexed _index
    );

    /**
     * @notice The preferred gateway for dependency type `_dependencyType` has been updated to `_gatewayAddress`.
     */
    event GatewayUpdated(
        ExternalAssetDependencyType indexed _dependencyType,
        string _gatewayAddress
    );

    /**
     * @notice The project id `_projectId` has had all external asset dependencies locked.
     * @dev This is a one-way operation. Once locked, the external asset dependencies cannot be updated.
     */
    event ProjectExternalAssetDependenciesLocked(uint256 indexed _projectId);

    /**
     * @notice An external asset dependency type. Can be one of IPFS, ARWEAVE, or ONCHAIN.
     */
    enum ExternalAssetDependencyType {
        IPFS,
        ARWEAVE,
        ONCHAIN
    }

    /**
     * @notice An external asset dependency. This is a struct that contains the CID of the dependency,
     * the type of the dependency, and the address of the bytecode for this dependency.
     */
    struct ExternalAssetDependency {
        string cid;
        ExternalAssetDependencyType dependencyType;
        address bytecodeAddress;
    }

    /**
     * @notice An external asset dependency with data. This is a convenience struct that contains the CID of the dependency,
     * the type of the dependency, the address of the bytecode for this dependency, and the data retrieved from this bytecode address.
     */
    struct ExternalAssetDependencyWithData {
        string cid;
        ExternalAssetDependencyType dependencyType;
        address bytecodeAddress;
        string data;
    }

    // preferredIPFSGateway is a url string
    function preferredIPFSGateway() external view returns (string memory);

    // preferredArweaveGateway is a url string
    function preferredArweaveGateway() external view returns (string memory);

    // updates the preferred IPFS gateway
    function updateIPFSGateway(string calldata _gateway) external;

    // updates the preferred Arweave gateway
    function updateArweaveGateway(string calldata _gateway) external;

    // locks the external asset dependencies for a project
    function lockProjectExternalAssetDependencies(uint256 _projectId) external;

    // updates the external asset dependency for a project at a given index
    function updateProjectExternalAssetDependency(
        uint256 _projectId,
        uint256 _index,
        string memory _cidOrData,
        ExternalAssetDependencyType _dependencyType
    ) external;

    // adds an external asset dependency for a project
    function addProjectExternalAssetDependency(
        uint256 _projectId,
        string memory _cidOrData,
        ExternalAssetDependencyType _dependencyType
    ) external;

    // removes an external asset dependency for a project at a given index
    function removeProjectExternalAssetDependency(
        uint256 _projectId,
        uint256 _index
    ) external;

    // getter function for project external asset dependencies
    function projectExternalAssetDependencyByIndex(
        uint256 _projectId,
        uint256 _index
    ) external view returns (ExternalAssetDependencyWithData memory);

    // getter function project external asset dependency count
    function projectExternalAssetDependencyCount(
        uint256 _projectId
    ) external view returns (uint256);
}

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);
}

File 17 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 18 of 21 : IRandomizer_V3CoreBase.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Creatd By: Art Blocks Inc.

pragma solidity ^0.8.0;

interface IRandomizer_V3CoreBase {
    /**
     * @notice This function is intended to be called by a core contract, and
     * the core contract can be assured that the randomizer will call back to
     * the calling contract to set the token hash seed for `_tokenId` via
     * `setTokenHash_8PT`.
     * @dev This function may revert if hash seed generation is improperly
     * configured (for example, if in polyptych mode, but no hash seed has been
     * previously configured).
     * @dev This function is not specifically gated to any specific caller, but
     * will only call back to the calling contract, `msg.sender`, to set the
     * specified token's hash seed.
     * A third party contract calling this function will not be able to set the
     * token hash seed on a different core contract.
     * @param _tokenId The token ID must be assigned a hash.
     */
    function assignTokenHash(uint256 _tokenId) external;
}

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

pragma solidity ^0.8.0;

/**
 * @title Art Blocks Script Storage Library
 * @notice Utilize contract bytecode as persistent storage for large chunks of script string data.
 *         This library is intended to have an external deployed copy that is released in the future,
 *         and, as such, has been designed to support both updated V1 (versioned, with purging removed)
 *         reads as well as backwards-compatible reads for both a) the unversioned "V0" storage contracts
 *         which were deployed by the original version of this libary and b) contracts that were deployed
 *         using one of the SSTORE2 implementations referenced below.
 *         For these pre-V1 storage contracts (which themselves did not have any explicit versioning semantics)
 *         backwards-compatible reads are optimistic, and only expected to work for contracts actually
 *         deployed by the original version of this library – and may fail ungracefully if attempted to be
 *         used to read from other contracts.
 *         This library is split into two components, intended to be updated in tandem, and thus included
 *         here in the same source file. One component is an internal library that is intended to be embedded
 *         directly into other contracts and provides all _write_ functionality. The other is a public library
 *         that is intended to be deployed as a standalone contract and provides all _read_ functionality.
 *
 * @author Art Blocks Inc.
 * @author Modified from 0xSequence (https://github.com/0xsequence/sstore2/blob/master/contracts/SSTORE2.sol)
 * @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
 *
 * @dev Compared to the above two rerferenced libraries, this contracts-as-storage implementation makes a few
 *      notably different design decisions:
 *      - uses the `string` data type for input/output on reads, rather than speaking in bytes directly
 *      - stores the "writer" address (library user) in the deployed contract bytes, which is useful for
 *        on-chain introspection and provenance purposes
 *      - stores a very simple versioning string in the deployed contract bytes, which captures the version
 *        of the library that was used to deploy the storage contract and useful for supporting future
 *        compatibility management as this library evolves (e.g. in response to EOF v1 migration plans)
 *      Also, given that much of this library is written in assembly, this library makes use of a slightly
 *      different convention (when compared to the rest of the Art Blocks smart contract repo) around
 *      pre-defining return values in some cases in order to simplify need to directly memory manage these
 *      return values.
 */

/**
 * @title Art Blocks Script Storage Library (Public, Reads)
 * @author Art Blocks Inc.
 * @notice The public library for reading from storage contracts. This library is intended to be deployed as a
 *         standalone contract, and provides all _read_ functionality.
 */
library BytecodeStorageReader {
    // Define the set of known valid version strings that may be stored in the deployed storage contract bytecode
    // note: These are all intentionally exactly 32-bytes and are null-terminated. Null-termination is used due
    //       to this being the standard expected formatting in common web3 tooling such as ethers.js. Please see
    //       the following for additional context: https://docs.ethers.org/v5/api/utils/strings/#Bytes32String
    // Used for storage contracts that were deployed by an unknown source
    bytes32 public constant UNKNOWN_VERSION_STRING =
        "UNKNOWN_VERSION_STRING_________ ";
    // Pre-dates versioning string, so this doesn't actually exist in any deployed contracts,
    // but is useful for backwards-compatible semantics with original version of this library
    bytes32 public constant V0_VERSION_STRING =
        "BytecodeStorage_V0.0.0_________ ";
    // The first versioned storage contract, deployed by an updated version of this library
    bytes32 public constant V1_VERSION_STRING =
        "BytecodeStorage_V1.0.0_________ ";
    // The current version of this library.
    bytes32 public constant CURRENT_VERSION = V1_VERSION_STRING;

    //---------------------------------------------------------------------------------------------------------------//
    // Starting Index | Size | Ending Index | Description                                                            //
    //---------------------------------------------------------------------------------------------------------------//
    // 0              | N/A  | 0            |                                                                        //
    // 0              | 1    | 1            | single byte opcode for making the storage contract non-executable      //
    // 1              | 32   | 33           | the 32 byte slot used for storing a basic versioning string            //
    // 33             | 32   | 65           | the 32 bytes for storing the deploying contract's (0-padded) address   //
    //---------------------------------------------------------------------------------------------------------------//
    // Define the offset for where the "meta bytes" end, and the "data bytes" begin. Note that this is a manually
    // calculated value, and must be updated if the above table is changed. It is expected that tests will fail
    // loudly if these values are not updated in-step with eachother.
    uint256 private constant VERSION_OFFSET = 1;
    uint256 private constant ADDRESS_OFFSET = 33;
    uint256 private constant DATA_OFFSET = 65;

    // Define the set of known *historic* offset values for where the "meta bytes" end, and the "data bytes" begin.
    // SSTORE2 deployed storage contracts take the general format of:
    // concat(0x00, data)
    // note: this is true for both variants of the SSTORE2 library
    uint256 private constant SSTORE2_DATA_OFFSET = 1;
    // V0 deployed storage contracts take the general format of:
    // concat(gated-cleanup-logic, deployer-address, data)
    uint256 private constant V0_ADDRESS_OFFSET = 72;
    uint256 private constant V0_DATA_OFFSET = 104;
    // V1 deployed storage contracts take the general format of:
    // concat(invalid opcode, version, deployer-address, data)
    uint256 private constant V1_ADDRESS_OFFSET = ADDRESS_OFFSET;
    uint256 private constant V1_DATA_OFFSET = DATA_OFFSET;

    /*//////////////////////////////////////////////////////////////
                               READ LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Read a string from contract bytecode
     * @param _address address of deployed contract with bytecode stored in the V0 or V1 format
     * @return data string read from contract bytecode
     * @dev This function performs input validation that the contract to read is in an expected format
     */
    function readFromBytecode(
        address _address
    ) public view returns (string memory data) {
        uint256 dataOffset = _bytecodeDataOffsetAt(_address);
        return string(readBytesFromBytecode(_address, dataOffset));
    }

    /**
     * @notice Read the bytes from contract bytecode that was written to the EVM using SSTORE2
     * @param _address address of deployed contract with bytecode stored in the SSTORE2 format
     * @return data bytes read from contract bytecode
     * @dev This function performs no input validation on the provided contract,
     *      other than that there is content to read (but not that its a "storage contract")
     */
    function readBytesFromSSTORE2Bytecode(
        address _address
    ) public view returns (bytes memory data) {
        return readBytesFromBytecode(_address, SSTORE2_DATA_OFFSET);
    }

    /**
     * @notice Read the bytes from contract bytecode, with an explicitly provided starting offset
     * @param _address address of deployed contract with bytecode stored in the V0 or V1 format
     * @param _offset offset to read from in contract bytecode, explicitly provided (not calculated)
     * @return data bytes read from contract bytecode
     * @dev This function performs no input validation on the provided contract,
     *      other than that there is content to read (but not that its a "storage contract")
     */
    function readBytesFromBytecode(
        address _address,
        uint256 _offset
    ) public view returns (bytes memory data) {
        // get the size of the bytecode
        uint256 bytecodeSize = _bytecodeSizeAt(_address);
        // handle case where address contains code < _offset
        if (bytecodeSize < _offset) {
            revert("ContractAsStorage: Read Error");
        }

        // handle case where address contains code >= dataOffset
        // decrement by dataOffset to account for header info
        uint256 size;
        unchecked {
            size = bytecodeSize - _offset;
        }

        assembly {
            // allocate free memory
            data := mload(0x40)
            // update free memory pointer
            // use and(x, not(0x1f) as cheaper equivalent to sub(x, mod(x, 0x20)).
            // adding 0x1f to size + logic above ensures the free memory pointer
            // remains word-aligned, following the Solidity convention.
            mstore(0x40, add(data, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            // store length of data in first 32 bytes
            mstore(data, size)
            // copy code to memory, excluding the deployer-address
            extcodecopy(_address, add(data, 0x20), _offset, size)
        }
    }

    /**
     * @notice Get address for deployer for given contract bytecode
     * @param _address address of deployed contract with bytecode stored in the V0 or V1 format
     * @return writerAddress address read from contract bytecode
     */
    function getWriterAddressForBytecode(
        address _address
    ) public view returns (address) {
        // get the size of the data
        uint256 bytecodeSize = _bytecodeSizeAt(_address);
        // the dataOffset for the bytecode
        uint256 addressOffset = _bytecodeAddressOffsetAt(_address);
        // handle case where address contains code < addressOffset + 32 (address takes a whole slot)
        if (bytecodeSize < (addressOffset + 32)) {
            revert("ContractAsStorage: Read Error");
        }

        assembly {
            // allocate free memory
            let writerAddress := mload(0x40)
            // shift free memory pointer by one slot
            mstore(0x40, add(mload(0x40), 0x20))
            // copy the 32-byte address of the data contract writer to memory
            // note: this relies on the assumption noted at the top-level of
            //       this file that the storage layout for the deployed
            //       contracts-as-storage contract looks like::
            //       | invalid opcode | version-string (unless v0) | deployer-address (padded) | data |
            extcodecopy(
                _address,
                writerAddress,
                addressOffset,
                0x20 // full 32-bytes, as address is expected to be zero-padded
            )
            return(
                writerAddress,
                0x20 // return size is entire slot, as it is zero-padded
            )
        }
    }

    /**
     * @notice Get version for given contract bytecode
     * @param _address address of deployed contract with bytecode stored in the V0 or V1 format
     * @return version version read from contract bytecode
     */
    function getLibraryVersionForBytecode(
        address _address
    ) public view returns (bytes32) {
        return _bytecodeVersionAt(_address);
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HELPER LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the size of the bytecode at address `_address`
     * @param _address address that may or may not contain bytecode
     * @return size size of the bytecode code at `_address`
     */
    function _bytecodeSizeAt(
        address _address
    ) private view returns (uint256 size) {
        assembly {
            size := extcodesize(_address)
        }
        if (size == 0) {
            revert("ContractAsStorage: Read Error");
        }
    }

    /**
     * @notice Returns the offset of the data in the bytecode at address `_address`
     * @param _address address that may or may not contain bytecode
     * @return dataOffset offset of data in bytecode if a known version, otherwise 0
     */
    function _bytecodeDataOffsetAt(
        address _address
    ) private view returns (uint256 dataOffset) {
        bytes32 version = _bytecodeVersionAt(_address);
        if (version == V1_VERSION_STRING) {
            dataOffset = V1_DATA_OFFSET;
        } else if (version == V0_VERSION_STRING) {
            dataOffset = V0_DATA_OFFSET;
        } else {
            // unknown version, revert
            revert("ContractAsStorage: Unsupported Version");
        }
    }

    /**
     * @notice Returns the offset of the address in the bytecode at address `_address`
     * @param _address address that may or may not contain bytecode
     * @return addressOffset offset of address in bytecode if a known version, otherwise 0
     */
    function _bytecodeAddressOffsetAt(
        address _address
    ) private view returns (uint256 addressOffset) {
        bytes32 version = _bytecodeVersionAt(_address);
        if (version == V1_VERSION_STRING) {
            addressOffset = V1_ADDRESS_OFFSET;
        } else if (version == V0_VERSION_STRING) {
            addressOffset = V0_ADDRESS_OFFSET;
        } else {
            // unknown version, revert
            revert("ContractAsStorage: Unsupported Version");
        }
    }

    /**
     * @notice Get version string for given contract bytecode
     * @param _address address of deployed contract with bytecode stored in the V0 or V1 format
     * @return version version string read from contract bytecode
     */
    function _bytecodeVersionAt(
        address _address
    ) private view returns (bytes32 version) {
        // get the size of the data
        uint256 bytecodeSize = _bytecodeSizeAt(_address);
        // handle case where address contains code < minimum expected version string size,
        // by returning early with the unknown version string
        if (bytecodeSize < (VERSION_OFFSET + 32)) {
            return UNKNOWN_VERSION_STRING;
        }

        assembly {
            // allocate free memory
            let versionString := mload(0x40)
            // shift free memory pointer by one slot
            mstore(0x40, add(mload(0x40), 0x20))
            // copy the 32-byte version string of the bytecode library to memory
            // note: this relies on the assumption noted at the top-level of
            //       this file that the storage layout for the deployed
            //       contracts-as-storage contract looks like:
            //       | invalid opcode | version-string (unless v0) | deployer-address (padded) | data |
            extcodecopy(
                _address,
                versionString,
                VERSION_OFFSET,
                0x20 // 32-byte version string
            )
            // note: must check against literal strings, as Yul does not allow for
            //       dynamic strings in switch statements.
            switch mload(versionString)
            case "BytecodeStorage_V1.0.0_________ " {
                version := V1_VERSION_STRING
            }
            case 0x2060486000396000513314601057fe5b60013614601957fe5b6000357fff0000 {
                // the v0 variant of this library pre-dates formal versioning w/ version strings,
                // so we check the first 32 bytes of the execution bytecode itself which
                // is static and known across all storage contracts deployed with the first version
                // of this library.
                version := V0_VERSION_STRING
            }
            default {
                version := UNKNOWN_VERSION_STRING
            }
        }
    }
}

/**
 * @title Art Blocks Script Storage Library (Internal, Writes)
 * @author Art Blocks Inc.
 * @notice The internal library for writing to storage contracts. This library is intended to be deployed
 *         within library client contracts that use this library to perform _write_ operations on storage.
 */
library BytecodeStorageWriter {
    /*//////////////////////////////////////////////////////////////
                           WRITE LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Write a string to contract bytecode
     * @param _data string to be written to contract. No input validation is performed on this parameter.
     * @param address_ address of deployed contract with bytecode stored in the V0 or V1 format
     */
    function writeToBytecode(
        string memory _data
    ) internal returns (address address_) {
        // prefix bytecode with
        bytes memory creationCode = abi.encodePacked(
            //---------------------------------------------------------------------------------------------------------------//
            // Opcode  | Opcode + Arguments  | Description  | Stack View                                                     //
            //---------------------------------------------------------------------------------------------------------------//
            // a.) creation code returns all code in the contract except for the first 11 (0B in hex) bytes, as these 11
            //     bytes are the creation code itself which we do not want to store in the deployed storage contract result
            //---------------------------------------------------------------------------------------------------------------//
            // 0x60    |  0x60_0B            | PUSH1 11     | codeOffset                                                     //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset                                                   //
            // 0x81    |  0x81               | DUP2         | codeOffset 0 codeOffset                                        //
            // 0x38    |  0x38               | CODESIZE     | codeSize codeOffset 0 codeOffset                               //
            // 0x03    |  0x03               | SUB          | (codeSize - codeOffset) 0 codeOffset                           //
            // 0x80    |  0x80               | DUP          | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset   //
            // 0x92    |  0x92               | SWAP3        | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset)   //
            // 0x59    |  0x59               | MSIZE        | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) //
            // 0x39    |  0x39               | CODECOPY     | 0 (codeSize - codeOffset)                                      //
            // 0xF3    |  0xF3               | RETURN       |                                                                //
            //---------------------------------------------------------------------------------------------------------------//
            // (11 bytes)
            hex"60_0B_59_81_38_03_80_92_59_39_F3",
            //---------------------------------------------------------------------------------------------------------------//
            // b.) ensure that the deployed storage contract is non-executeable (first opcode is the `invalid` opcode)
            //---------------------------------------------------------------------------------------------------------------//
            //---------------------------------------------------------------------------------------------------------------//
            // 0xFE    |  0xFE               | INVALID      |                                                                //
            //---------------------------------------------------------------------------------------------------------------//
            // (1 byte)
            hex"FE",
            //---------------------------------------------------------------------------------------------------------------//
            // c.) store the version string, which is already represented as a 32-byte value
            //---------------------------------------------------------------------------------------------------------------//
            // (32 bytes)
            BytecodeStorageReader.CURRENT_VERSION,
            //---------------------------------------------------------------------------------------------------------------//
            // d.) store the deploying-contract's address with 0-padding to fit a 20-byte address into a 32-byte slot
            //---------------------------------------------------------------------------------------------------------------//
            // (12 bytes)
            hex"00_00_00_00_00_00_00_00_00_00_00_00",
            // (20 bytes)
            address(this),
            // uploaded data (stored as bytecode) comes last
            _data
        );

        assembly {
            // deploy a new contract with the generated creation code.
            // start 32 bytes into creationCode to avoid copying the byte length.
            address_ := create(0, add(creationCode, 0x20), mload(creationCode))
        }

        // address must be non-zero if contract was deployed successfully
        require(address_ != address(0), "ContractAsStorage: Write Error");
    }
}

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

pragma solidity ^0.8.0;

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

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

File 21 of 21 : ERC721_PackedHashSeed.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "@openzeppelin-4.7/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin-4.7/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin-4.7/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@openzeppelin-4.7/contracts/utils/Address.sol";
import "@openzeppelin-4.7/contracts/utils/Context.sol";
import "@openzeppelin-4.7/contracts/utils/Strings.sol";
import "@openzeppelin-4.7/contracts/utils/introspection/ERC165.sol";

/**
 * @dev Forked version of the OpenZeppelin v4.7.1 ERC721 contract. Utilizes a
 * struct to pack owner and hash seed into a single storage slot.
 * ---------------------
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721_PackedHashSeed is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    /// struct to pack a token owner and hash seed into same storage slot
    struct OwnerAndHashSeed {
        // 20 bytes for address of token's owner
        address owner;
        // remaining 12 bytes allocated to token hash seed
        bytes12 hashSeed;
    }

    /// mapping of token ID to OwnerAndHashSeed
    /// @dev visibility internal so inheriting contracts can access
    mapping(uint256 => OwnerAndHashSeed) internal _ownersAndHashSeeds;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(
        address owner
    ) public view virtual override returns (uint256) {
        require(
            owner != address(0),
            "ERC721: address zero is not a valid owner"
        );
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(
        uint256 tokenId
    ) public view virtual override returns (address) {
        address owner = _ownersAndHashSeeds[tokenId].owner;
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(
        uint256 tokenId
    ) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory baseURI = _baseURI();
        return
            bytes(baseURI).length > 0
                ? string(abi.encodePacked(baseURI, tokenId.toString()))
                : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721_PackedHashSeed.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not token owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(
        uint256 tokenId
    ) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(
        address operator,
        bool approved
    ) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(
        address owner,
        address operator
    ) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(
            _isApprovedOrOwner(_msgSender(), tokenId),
            "ERC721: caller is not token owner nor approved"
        );

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public virtual override {
        require(
            _isApprovedOrOwner(_msgSender(), tokenId),
            "ERC721: caller is not token owner nor approved"
        );
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(
            _checkOnERC721Received(from, to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _ownersAndHashSeeds[tokenId].owner != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(
        address spender,
        uint256 tokenId
    ) internal view virtual returns (bool) {
        address owner = ERC721_PackedHashSeed.ownerOf(tokenId);
        return (spender == owner ||
            isApprovedForAll(owner, spender) ||
            getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _ownersAndHashSeeds[tokenId].owner = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721_PackedHashSeed.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _ownersAndHashSeeds[tokenId].owner;

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(
            ERC721_PackedHashSeed.ownerOf(tokenId) == from,
            "ERC721: transfer from incorrect owner"
        );
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _ownersAndHashSeeds[tokenId].owner = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721_PackedHashSeed.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try
                IERC721Receiver(to).onERC721Received(
                    _msgSender(),
                    from,
                    tokenId,
                    data
                )
            returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert(
                        "ERC721: transfer to non ERC721Receiver implementer"
                    );
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 25
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/libs/v0.8.x/BytecodeStorageV1.sol": {
      "BytecodeStorageReader": "0xf0585df582a0ad119f1616fb82f3b449a98eecd5"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"string","name":"_tokenName","type":"string"},{"internalType":"string","name":"_tokenSymbol","type":"string"},{"internalType":"address","name":"_renderProviderAddress","type":"address"},{"internalType":"address","name":"_platformProviderAddress","type":"address"},{"internalType":"address","name":"_randomizerContract","type":"address"},{"internalType":"address","name":"_adminACLContract","type":"address"},{"internalType":"uint248","name":"_startingProjectId","type":"uint248"},{"internalType":"bool","name":"_autoApproveArtistSplitProposals","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"AcceptedArtistAddressesAndSplits","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_index","type":"uint256"}],"name":"ExternalAssetDependencyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_index","type":"uint256"},{"indexed":false,"internalType":"string","name":"_cid","type":"string"},{"indexed":false,"internalType":"enum IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType","name":"_dependencyType","type":"uint8"},{"indexed":false,"internalType":"uint24","name":"_externalAssetDependencyCount","type":"uint24"}],"name":"ExternalAssetDependencyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType","name":"_dependencyType","type":"uint8"},{"indexed":false,"internalType":"string","name":"_gatewayAddress","type":"string"}],"name":"GatewayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_currentMinter","type":"address"}],"name":"MinterUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_field","type":"bytes32"}],"name":"PlatformUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"ProjectExternalAssetDependenciesLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"_update","type":"bytes32"}],"name":"ProjectUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_projectId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_artistAddress","type":"address"},{"indexed":false,"internalType":"address","name":"_additionalPayeePrimarySales","type":"address"},{"indexed":false,"internalType":"uint256","name":"_additionalPayeePrimarySalesPercentage","type":"uint256"},{"indexed":false,"internalType":"address","name":"_additionalPayeeSecondarySales","type":"address"},{"indexed":false,"internalType":"uint256","name":"_additionalPayeeSecondarySalesPercentage","type":"uint256"}],"name":"ProposedArtistAddressesAndSplits","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"string","name":"_projectName","type":"string"},{"internalType":"address payable","name":"_artistAddress","type":"address"}],"name":"addProject","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_cidOrData","type":"string"},{"internalType":"enum IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType","name":"_dependencyType","type":"uint8"}],"name":"addProjectExternalAssetDependency","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_script","type":"string"}],"name":"addProjectScript","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_contract","type":"address"},{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"adminACLAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"adminACLContract","outputs":[{"internalType":"contract IAdminACLV0","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address payable","name":"_artistAddress","type":"address"},{"internalType":"address payable","name":"_additionalPayeePrimarySales","type":"address"},{"internalType":"uint256","name":"_additionalPayeePrimarySalesPercentage","type":"uint256"},{"internalType":"address payable","name":"_additionalPayeeSecondarySales","type":"address"},{"internalType":"uint256","name":"_additionalPayeeSecondarySalesPercentage","type":"uint256"}],"name":"adminAcceptArtistAddressesAndSplits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"artblocksDependencyRegistryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"autoApproveArtistSplitProposals","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"coreType","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"coreVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"defaultBaseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"forbidNewProjects","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"getHistoricalRandomizerAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"getPrimaryRevenueSplits","outputs":[{"internalType":"uint256","name":"renderProviderRevenue_","type":"uint256"},{"internalType":"address payable","name":"renderProviderAddress_","type":"address"},{"internalType":"uint256","name":"platformProviderRevenue_","type":"uint256"},{"internalType":"address payable","name":"platformProviderAddress_","type":"address"},{"internalType":"uint256","name":"artistRevenue_","type":"uint256"},{"internalType":"address payable","name":"artistAddress_","type":"address"},{"internalType":"uint256","name":"additionalPayeePrimaryRevenue_","type":"uint256"},{"internalType":"address payable","name":"additionalPayeePrimaryAddress_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getRoyalties","outputs":[{"internalType":"address payable[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"bps","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"isMintWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"lockProjectExternalAssetDependencies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address","name":"_by","type":"address"}],"name":"mint_Ecf","outputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minterContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newProjectsForbidden","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextProjectId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numHistoricalRandomizers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformProviderPrimarySalesAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformProviderPrimarySalesPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformProviderSecondarySalesAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformProviderSecondarySalesBPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"preferredArweaveGateway","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"preferredIPFSGateway","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectArtistPaymentInfo","outputs":[{"internalType":"address","name":"artistAddress","type":"address"},{"internalType":"address","name":"additionalPayeePrimarySales","type":"address"},{"internalType":"uint256","name":"additionalPayeePrimarySalesPercentage","type":"uint256"},{"internalType":"address","name":"additionalPayeeSecondarySales","type":"address"},{"internalType":"uint256","name":"additionalPayeeSecondarySalesPercentage","type":"uint256"},{"internalType":"uint256","name":"secondaryMarketRoyaltyPercentage","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectDetails","outputs":[{"internalType":"string","name":"projectName","type":"string"},{"internalType":"string","name":"artist","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"website","type":"string"},{"internalType":"string","name":"license","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"projectExternalAssetDependencyByIndex","outputs":[{"components":[{"internalType":"string","name":"cid","type":"string"},{"internalType":"enum IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType","name":"dependencyType","type":"uint8"},{"internalType":"address","name":"bytecodeAddress","type":"address"},{"internalType":"string","name":"data","type":"string"}],"internalType":"struct IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyWithData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectExternalAssetDependencyCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectIdToAdditionalPayeePrimarySales","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectIdToAdditionalPayeePrimarySalesPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectIdToAdditionalPayeeSecondarySales","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectIdToAdditionalPayeeSecondarySalesPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectIdToArtistAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectIdToSecondaryMarketRoyaltyPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"projectScriptByIndex","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"projectScriptBytecodeAddressByIndex","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectScriptDetails","outputs":[{"internalType":"string","name":"scriptTypeAndVersion","type":"string"},{"internalType":"string","name":"aspectRatio","type":"string"},{"internalType":"uint256","name":"scriptCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectStateData","outputs":[{"internalType":"uint256","name":"invocations","type":"uint256"},{"internalType":"uint256","name":"maxInvocations","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"uint256","name":"completedTimestamp","type":"uint256"},{"internalType":"bool","name":"locked","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"projectURIInfo","outputs":[{"internalType":"string","name":"projectBaseURI","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address payable","name":"_artistAddress","type":"address"},{"internalType":"address payable","name":"_additionalPayeePrimarySales","type":"address"},{"internalType":"uint256","name":"_additionalPayeePrimarySalesPercentage","type":"uint256"},{"internalType":"address payable","name":"_additionalPayeeSecondarySales","type":"address"},{"internalType":"uint256","name":"_additionalPayeeSecondarySalesPercentage","type":"uint256"}],"name":"proposeArtistPaymentAddressesAndSplits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"proposedArtistAddressesAndSplitsHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randomizerContract","outputs":[{"internalType":"contract IRandomizer_V3CoreBase","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"removeProjectExternalAssetDependency","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"removeProjectLastScript","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renderProviderPrimarySalesAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renderProviderPrimarySalesPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renderProviderSecondarySalesAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renderProviderSecondarySalesBPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes32","name":"_hashSeed","type":"bytes32"}],"name":"setTokenHash_8PT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startingProjectId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"toggleProjectIsActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"name":"toggleProjectIsPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenIdToHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenIdToHashSeed","outputs":[{"internalType":"bytes12","name":"","type":"bytes12"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenIdToProjectId","outputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_artblocksDependencyRegistryAddress","type":"address"}],"name":"updateArtblocksDependencyRegistryAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_gateway","type":"string"}],"name":"updateArweaveGateway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_defaultBaseURI","type":"string"}],"name":"updateDefaultBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_gateway","type":"string"}],"name":"updateIPFSGateway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"updateMinterContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"address payable","name":"_artistAddress","type":"address"}],"name":"updateProjectArtistAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_projectArtistName","type":"string"}],"name":"updateProjectArtistName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_aspectRatio","type":"string"}],"name":"updateProjectAspectRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_newBaseURI","type":"string"}],"name":"updateProjectBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_projectDescription","type":"string"}],"name":"updateProjectDescription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"string","name":"_cidOrData","type":"string"},{"internalType":"enum IGenArt721CoreContractV3_Engine_Flex.ExternalAssetDependencyType","name":"_dependencyType","type":"uint8"}],"name":"updateProjectExternalAssetDependency","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_projectLicense","type":"string"}],"name":"updateProjectLicense","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint24","name":"_maxInvocations","type":"uint24"}],"name":"updateProjectMaxInvocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_projectName","type":"string"}],"name":"updateProjectName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_scriptId","type":"uint256"},{"internalType":"string","name":"_script","type":"string"}],"name":"updateProjectScript","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"bytes32","name":"_scriptTypeAndVersion","type":"bytes32"}],"name":"updateProjectScriptType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"uint256","name":"_secondMarketRoyalty","type":"uint256"}],"name":"updateProjectSecondaryMarketRoyaltyPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_projectId","type":"uint256"},{"internalType":"string","name":"_projectWebsite","type":"string"}],"name":"updateProjectWebsite","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"renderProviderPrimarySalesPercentage_","type":"uint256"},{"internalType":"uint256","name":"platformProviderPrimarySalesPercentage_","type":"uint256"}],"name":"updateProviderPrimarySalesPercentages","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_renderProviderPrimarySalesAddress","type":"address"},{"internalType":"address payable","name":"_renderProviderSecondarySalesAddress","type":"address"},{"internalType":"address payable","name":"_platformProviderPrimarySalesAddress","type":"address"},{"internalType":"address payable","name":"_platformProviderSecondarySalesAddress","type":"address"}],"name":"updateProviderSalesAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_renderProviderSecondarySalesBPS","type":"uint256"},{"internalType":"uint256","name":"_platformProviderSecondarySalesBPS","type":"uint256"}],"name":"updateProviderSecondarySalesBPS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_randomizerAddress","type":"address"}],"name":"updateRandomizerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106104825760003560e01c806376ee6fab1161025a578063b7b04fae1161014d578063b7b04fae14610b5c578063b7ba527d14610b6f578063b88d4fde14610b77578063b971136814610b8a578063ba3c234514610b92578063bb3bafd614610ba5578063bd3d10e714610bc6578063c34a03b514610bd9578063c6c7c5bf14610bec578063c87b56dd14610bff578063cc90e72514610c12578063d03c390c14610c25578063d50f513814610c38578063db21a84614610c4b578063db2ff86114610c5e578063e6032df214610c71578063e935b7b114610c98578063e985e9c514610ca9578063eb9cd5d414610cbc578063ed8abfda14610cde578063f2fde38b14610d08578063f313d73314610d1b578063f6cd39e314610db1578063f851a44014610db9578063f893c07b14610dc157600080fd5b806376ee6fab146108db5780638639415b146108ee5780638c3c9cdd146109495780638da5cb5b1461095c5780638dd91a561461096457806392f002331461098857806395d89b411461099b5780639a02e4fa146109a3578063a11ec70a146109da578063a22cb465146109ed578063a3b2cca614610a00578063a47d29cb14610a13578063abcbb7b414610a3f578063ac11fa1c14610a47578063acad012414610a67578063acd4c66f14610a7a578063ad0305ce14610a8c578063ad28329014610aae578063ad2cdfc414610adb578063ad576c4514610b08578063ae45ad9814610b1b578063b1656ba314610b23578063b168762214610b36578063b75395e014610b4957600080fd5b806328ec8f501161037d57806328ec8f50146106d25780632b274166146106ff5780632b65e67d146107125780632b8258c5146107255780632d9c02051461072e57806330ef4c5f1461074157806336c7c12c14610753578063378599631461076657806338c8e468146107795780633bb22553146107a55780633e48e848146107ae57806341814cd3146107c157806342842e0e146107d457806348337282146107e75780634e1d64af146107fa5780635464c309146108025780635508fd521461081557806358b9a5a914610829578063621a1f74146108615780636352211e1461087457806366e462371461088757806369d14faf1461089a5780636c907b7f146108ad57806370a08231146108c0578063715018a6146108d357600080fd5b80611e3c146104875780615de51461049c5780630132c697146104c257806301856fd4146104d557806301ffc9a7146104e857806304143a5c1461050b57806306fdde0314610513578063081812fc14610528578063095ea7b3146105485780630d1706731461055b5780630e79c9281461056e5780630ea5613f146105815780630ebeb0ee146105cb57806310a9ef18146105f457806317df5366146106075780631ab6014c1461061a5780631b689c0b1461062d5780631c05cad7146106405780631e9bef46146106535780632302cbda14610666578063230448b11461067957806323b872dd1461068c57806325b75d681461069f5780632642c6b6146106b2575b600080fd5b61049a610495366004614ec5565b610de8565b005b6104af6104aa366004614efc565b610f17565b6040519081526020015b60405180910390f35b61049a6104d0366004614f3e565b6111bb565b61049a6104e3366004614ec5565b6112e1565b6104fb6104f6366004614f8c565b611395565b60405190151581526020016104b9565b61049a6113c0565b61051b611428565b6040516104b99190615000565b61053b610536366004615013565b6114ba565b6040516104b9919061502c565b61049a610556366004615040565b6114e1565b61049a610569366004615137565b6115f6565b61049a61057c366004615137565b61165b565b61059461058f366004615013565b611857565b60408051968752602087019590955292151593850193909352151560608401526080830191909152151560a082015260c0016104b9565b61053b6105d9366004615013565b6000908152600e60205260409020546001600160a01b031690565b61049a610602366004615191565b6118ba565b60075461053b906001600160a01b031681565b60115461053b906001600160a01b031681565b6104af61063b366004615013565b611a85565b61049a61064e3660046151f1565b611a94565b600a5461053b906001600160a01b031681565b61049a61067436600461524d565b611ad4565b6104fb610687366004615281565b611af9565b61049a61069a3660046152c1565b611ba5565b61049a6106ad366004615137565b611bd6565b6106c56106c0366004614ec5565b611c3e565b6040516104b9919061533a565b6104af6106e0366004615013565b6000908152600e6020526040902060010154600160a01b900460ff1690565b61049a61070d3660046153a4565b611db3565b61049a6107203660046153c1565b611e1f565b6104af60155481565b61051b61073c366004615013565b612185565b601154600160a01b900460ff166104af565b60085461053b906001600160a01b031681565b61049a610774366004615137565b61222a565b61053b610787366004615013565b6000908152600e60205260409020600101546001600160a01b031690565b6104af60135481565b61049a6107bc366004615137565b612278565b60125461053b906001600160a01b031681565b61049a6107e23660046152c1565b6122cf565b61049a6107f53660046153a4565b6122ea565b61051b61234d565b61049a610810366004615428565b612366565b6017546104fb90600160f81b900460ff1681565b61053b610837366004614ec5565b6000918252600b60209081526040808420928452600990920190529020546001600160a01b031690565b6104af61086f366004615013565b6123c4565b61053b610882366004615013565b612426565b61049a610895366004615499565b61245b565b61049a6108a83660046154ef565b612611565b61049a6108bb3660046153a4565b61268a565b6104af6108ce3660046153a4565b6126ac565b61049a612732565b61049a6108e93660046153c1565b61274a565b6109016108fc366004614ec5565b612912565b604080519889526001600160a01b0397881660208a015288019590955292851660608701526080860191909152831660a085015260c08401521660e0820152610100016104b9565b61051b610957366004614ec5565b612a1b565b61053b612a7a565b610977610972366004615013565b612a8e565b6040516104b9959493929190615514565b60165461053b906001600160a01b031681565b61051b612d2b565b6109cd6109b1366004615013565b600090815260026020526040902054600160a01b900460a01b90565b6040516104b99190615581565b61049a6109e8366004615013565b612d3a565b61049a6109fb3660046155a4565b612d92565b61049a610a0e366004615137565b612da1565b61053b610a21366004615013565b6000908152600e60205260409020600201546001600160a01b031690565b61051b612ea8565b6104af610a55366004615013565b600f6020526000908152604090205481565b61049a610a75366004615137565b612f36565b601054600160a01b900460ff166104af565b6104fb610a9a3660046153a4565b6016546001600160a01b0390811691161490565b6104af610abc366004615013565b6000908152600b60205260409020600a0154610100900462ffffff1690565b6104af610ae9366004615013565b6000908152600e6020526040902060020154600160a01b900460ff1690565b61049a610b16366004614ec5565b613009565b61051b613192565b61049a610b313660046155d2565b6131bb565b61053b610b44366004615013565b6132a0565b60105461053b906001600160a01b031681565b61049a610b6a366004615137565b613318565b61051b613383565b61049a610b85366004615621565b613390565b6009546104af565b61049a610ba0366004615013565b6133c2565b610bb8610bb3366004615013565b613422565b6040516104b99291906156a0565b61049a610bd4366004614ec5565b6136b6565b61049a610be7366004614ec5565b613782565b60145461053b906001600160a01b031681565b61051b610c0d366004615013565b613823565b61049a610c20366004615724565b61390b565b61049a610c33366004615013565b613a60565b61049a610c46366004615428565b613ac8565b61049a610c59366004614ec5565b613aed565b61049a610c6c366004615013565b613b8c565b6104fb7f000000000000000000000000000000000000000000000000000000000000000181565b6017546001600160f81b03166104af565b6104fb610cb736600461576a565b613c99565b610ccf610cca366004615013565b613cc7565b6040516104b993929190615798565b6104af610cec366004615013565b6000908152600e6020526040902054600160a01b900460ff1690565b61049a610d163660046153a4565b613d93565b610d73610d29366004615013565b6000908152600e60205260409020600281015481546001909201546001600160a01b03808316948482169460ff600160a01b95869004811695938516948490048116939091041690565b604080516001600160a01b039788168152958716602087015285019390935293166060830152608082019290925260a081019190915260c0016104b9565b61051b613e09565b61053b613e16565b6104af7f000000000000000000000000000000000000000000000000000000000000000181565b610df182613e20565b60008281526002602052604090206008546001600160a01b03163314610e585760405162461bcd60e51b815260206004820152601760248201527613db9b1e481c985b991bdb5a5e995c881b585e481cd95d604a1b60448201526064015b60405180910390fd5b8054600160a01b900460a01b6001600160a01b03191615610eb45760405162461bcd60e51b8152602060048201526016602482015275151bdad95b881a185cda08185b1c9958591e481cd95d60521b6044820152606401610e4f565b81610ef55760405162461bcd60e51b8152602060048201526011602482015270139bc81e995c9bc81a185cda081cd95959607a1b6044820152606401610e4f565b805460a09290921c600160a01b026001600160a01b0390921691909117905550565b6016546000906001600160a01b03163314610f745760405162461bcd60e51b815260206004820152601e60248201527f4d757374206d696e742066726f6d206d696e74657220636f6e747261637400006044820152606401610e4f565b6000838152600b60205260409020805462ffffff808216916001830191630100000090910416808310610fe95760405162461bcd60e51b815260206004820152601f60248201527f4d757374206e6f7420657863656564206d617820696e766f636174696f6e73006044820152606401610e4f565b8354600160881b900460ff168061101c57506000878152600e60205260409020600201546001600160a01b038781169116145b6110685760405162461bcd60e51b815260206004820181905260248201527f50726f6a656374206d75737420657869737420616e64206265206163746976656044820152606401610e4f565b8354600160901b900460ff16158061109c57506000878152600e60205260409020600201546001600160a01b038781169116145b6110e05760405162461bcd60e51b8152602060048201526015602482015274283ab931b430b9b2b99030b932903830bab9b2b21760591b6044820152606401610e4f565b835462ffffff191662ffffff8381169182178655848116620f42408a02019190831690036111115761111188613e6f565b61111b8982613ec7565b600854604051635b140b8d60e11b8152600481018390526001600160a01b039091169063b628171a90602401600060405180830381600087803b15801561116157600080fd5b505af1158015611175573d6000803e3d6000fd5b50506040518392506001600160a01b038c1691507f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d412139688590600090a398975050505050505050565b6111c482613ff9565b6000828152600b60205260409020805462ffffff80821691630100000090048116908416106112345760405162461bcd60e51b815260206004820152601c60248201527b4f6e6c79206d6178496e766f636174696f6e7320646563726561736560201b6044820152606401610e4f565b808362ffffff1610156112805760405162461bcd60e51b81526020600482015260146024820152734f6e6c792067746520696e766f636174696f6e7360601b6044820152606401610e4f565b815465ffffff0000001916630100000062ffffff8516021782556040516d6d6178496e766f636174696f6e7360901b908590600080516020615f1283398151915290600090a3808362ffffff16036112db576112db84613e6f565b50505050565b6112ea82614050565b6112fa8262615bf560e21b614098565b6000828152600b602052604090819020906113189083906001614112565b6113615760405162461bcd60e51b815260206004820152601a60248201527901b5d5cdd0818dbdb9d185a5b88195e1858dd1b1e481bdb9948160361b6044820152606401610e4f565b60078101829055604051697363726970745479706560b01b908490600080516020615f1283398151915290600090a3505050565b60006001600160e01b03198216635d9dd7eb60e11b14806113ba57506113ba82614185565b92915050565b6113d06301050e9760e21b6141d5565b601754600160f81b900460ff161561141e5760405162461bcd60e51b815260206004820152601160248201527020b63932b0b23c903337b93134b23232b760791b6044820152606401610e4f565b611426614225565b565b606060008054611437906157ce565b80601f0160208091040260200160405190810160405280929190818152602001828054611463906157ce565b80156114b05780601f10611485576101008083540402835291602001916114b0565b820191906000526020600020905b81548152906001019060200180831161149357829003601f168201915b5050505050905090565b60006114c58261427d565b506000908152600460205260409020546001600160a01b031690565b60006114ec82612426565b9050806001600160a01b0316836001600160a01b0316036115595760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610e4f565b336001600160a01b038216148061157557506115758133613c99565b6115e75760405162461bcd60e51b815260206004820152603e60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c00006064820152608401610e4f565b6115f183836142a2565b505050565b6115ff82614050565b61161082630d17067360e01b614098565b61161981614310565b6000828152600b602052604090206001016116348282615865565b50604051636e616d6560e01b908390600080516020615f1283398151915290600090a35050565b61166482614050565b611675826301cf392560e31b614098565b61167e81614310565b80518190600b8111156116d25760405162461bcd60e51b815260206004820152601c60248201527b41737065637420726174696f20666f726d617420746f6f206c6f6e6760201b6044820152606401610e4f565b60008060005b838110156117be5760008582815181106116f4576116f461591e565b01602001516001600160f81b0319169050600360fc1b81108015906117275750603960f81b6001600160f81b0319821611155b156117365760019250506117ac565b6001600160f81b03198116601760f91b0361175a578361175a5760019350506117ac565b60405162461bcd60e51b815260206004820152602160248201527f496d70726f7065726c7920666f726d61747465642061737065637420726174696044820152606f60f81b6064820152608401610e4f565b806117b68161594a565b9150506116d8565b508061180a5760405162461bcd60e51b815260206004820152601b60248201527a41737065637420726174696f20686173206e6f206e756d6265727360281b6044820152606401610e4f565b6000868152600b602052604090206008016118258682615865565b506040516a617370656374526174696f60a81b908790600080516020615f1283398151915290600090a3505050505050565b6000818152600b60205260408120805462ffffff808216936301000000830490911692600160881b830460ff90811693600160901b810490911692600160481b9091046001600160401b0316916118ad8861435f565b1591505091939550919395565b6118c3846143a9565b6118d4846302153de360e31b614098565b6000848152600b60205260409020600a0154610100900462ffffff1680841061190f5760405162461bcd60e51b8152600401610e4f90615963565b6000858152600b60208181526040808420888552909201905290206001808201805460ff81169286929160ff19169083600281111561195057611950615302565b0217905550600284600281111561196957611969615302565b03611a1857600281600281111561198257611982615302565b146119ba57604080516020808201835260008083528a8152600b8083528482208b83520190915291909120906119b89082615865565b505b6119c38561440a565b6000888152600b602081815260408084208b8552909201815281832060010180546001600160a01b039590951661010002610100600160a81b0319909516949094179093558051928301905281529450611a3f565b6000878152600b602081815260408084208a855290920190529020611a3d8682615865565b505b85877f1628bf0d5f04426de03959d1da1357d8210b3b99e35281256ab7e9bd55dae949878787604051611a7493929190615995565b60405180910390a350505050505050565b60006113ba620f4240836159e0565b611aa4631c05cad760e01b6141d5565b611aad846144ba565b611ab6836144ba565b611abf826144ba565b611ac8816144ba565b6112db8484848461450e565b611ae463118165ed60e11b6141d5565b611aed81614310565b611af681614590565b50565b600080611b04612a7a565b6001600160a01b031614158015611b9d5750600a546040516217798b60e61b81526001600160a01b03868116600483015285811660248301526001600160e01b031985166044830152909116906305de62c0906064016020604051808303816000875af1158015611b79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b9d91906159f4565b949350505050565b611baf33826145ca565b611bcb5760405162461bcd60e51b8152600401610e4f90615a11565b6115f1838383614628565b611bdf82614050565b611bf0826304b6ebad60e31b614098565b611bf981614310565b6000828152600b60205260409020600501611c148282615865565b50604051666c6963656e736560c81b908390600080516020615f1283398151915290600090a35050565b611c6a604080516080810190915260608152602081016000815260006020820152606060409091015290565b6000838152600b602081815260408084208685529092019052908190206001810154825160808101909352815491926101009091046001600160a01b03169181908490611cb6906157ce565b80601f0160208091040260200160405190810160405280929190818152602001828054611ce2906157ce565b8015611d2f5780601f10611d0457610100808354040283529160200191611d2f565b820191906000526020600020905b815481529060010190602001808311611d1257829003601f168201915b5050509183525050600184015460209091019060ff166002811115611d5657611d56615302565b81526001600160a01b03831660208201526040016002600185015460ff166002811115611d8557611d85615302565b14611d9f5760405180602001604052806000815250611da8565b611da8836147c4565b905295945050505050565b611dc3631593a0b360e11b6141d5565b611dcc816144ba565b600780546001600160a01b0319166001600160a01b03831617905560405178646570656e64656e637952656769737472794164647265737360381b90600080516020615ef283398151915290600090a250565b611e2886614843565b611e3186613ff9565b611e3a856144ba565b6000868152600e6020526040902060648411801590611e5a575060648211155b611e945760405162461bcd60e51b815260206004820152600b60248201526a4d6178206f66203130302560a81b6044820152606401610e4f565b831580611ea957506001600160a01b03851615155b611ef55760405162461bcd60e51b815260206004820152601d60248201527f5072696d617279207061796565206973207a65726f20616464726573730000006044820152606401610e4f565b811580611f0a57506001600160a01b03831615155b611f565760405162461bcd60e51b815260206004820152601f60248201527f5365636f6e64617279207061796565206973207a65726f2061646472657373006044820152606401610e4f565b867f6ff7d102bb3657a26dcbbcd299d821a066718a7cf76ae7cd98279f18b74da8ac8787878787604051611f8e959493929190615a5f565b60405180910390a27f00000000000000000000000000000000000000000000000000000000000000018061203457600282015482546001600160a01b03918216898316149160009181169089161480611fee57506001600160a01b038816155b60018501549091506000906001600160a01b038881169116148061201957506001600160a01b038716155b90508280156120255750815b801561202e5750805b93505050505b801561213c576000801b600f60008a815260200190815260200160002081905550868260020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550858260000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550848260020160146101000a81548160ff021916908360ff160217905550838260010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550828260010160146101000a81548160ff021916908360ff160217905550877fc582d05e1da854143bd3271ef4529d79cf5a69fc6057ae320f357acfd291b73860405160405180910390a261217b565b8686868686604051602001612155959493929190615a5f565b60408051601f19818403018152918152815160209283012060008b8152600f9093529120555b5050505050505050565b6000818152600b602052604090206006018054606091906121a5906157ce565b80601f01602080910402602001604051908101604052809291908181526020018280546121d1906157ce565b801561221e5780601f106121f35761010080835404028352916020019161221e565b820191906000526020600020905b81548152906001019060200180831161220157829003601f168201915b50505050509050919050565b61223382613ff9565b6000828152600b6020526040902060040161224e8282615865565b50604051667765627369746560c81b908390600080516020615f1283398151915290600090a35050565b61228182613ff9565b61228a81614310565b6000828152600b602052604090206006016122a58282615865565b50604051666261736555524960c81b908390600080516020615f1283398151915290600090a35050565b6115f183838360405180602001604052806000815250613390565b6122fa632419b94160e11b6141d5565b612303816144ba565b601680546001600160a01b0319166001600160a01b0383169081179091556040517fad0f299ec81a386c98df0ac27dae11dd020ed1b56963c53a7292e7a3a314539a90600090a250565b6060612361651d8ccb8c4b8d60d21b6148c5565b905090565b612376635464c30960e01b6141d5565b600d612383828483615a92565b5060015b7f897675a9bb2e78c7082f6dd757f2db5e1d07724c1f2d46fea1da4e17e5061afd83836040516123b8929190615b4c565b60405180910390a25050565b600081815260026020526040812054600160a01b900460a01b6001600160a01b0319811682036123f75750600092915050565b806040516020016124089190615581565b60405160208183030381529060405280519060200120915050919050565b6000818152600260205260408120546001600160a01b0316806113ba5760405162461bcd60e51b8152600401610e4f90615b7b565b612464836143a9565b612475836366e4623760e01b614098565b6000838152600b60205260408120600a0154610100900462ffffff169060028360028111156124a6576124a6615302565b036124c9576124b48461440a565b90506040518060200160405280600081525093505b600060405180606001604052808681526020018560028111156124ee576124ee615302565b81526001600160a01b0384166020918201526000888152600b808352604080832062ffffff89168452909101909252208151919250829181906125319082615865565b50602082015160018083018054909160ff199091169083600281111561255957612559615302565b021790555060409190910151600191820180546001600160a01b0390921661010002610100600160a81b0319909216919091179055612599908490615bad565b6000878152600b60205260409020600a01805463ffffff00191661010062ffffff938416021790558316867f1628bf0d5f04426de03959d1da1357d8210b3b99e35281256ab7e9bd55dae94987876125f2886001615bad565b60405161260193929190615995565b60405180910390a3505050505050565b61261a82614843565b61262b826369d14faf60e01b614930565b612634816144ba565b6000828152600e602052604080822060020180546001600160a01b0319166001600160a01b038516179055516c6172746973744164647265737360981b918491600080516020615f128339815191529190a35050565b61269a636c907b7f60e01b6141d5565b6126a3816144ba565b611af6816149e7565b60006001600160a01b0382166127165760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608401610e4f565b506001600160a01b031660009081526003602052604090205490565b61273a614a6e565b612742614225565b611426614acd565b61275386614843565b612764866376ee6fab60e01b614930565b61276d856144ba565b8484848484604051602001612786959493929190615a5f565b60408051601f1981840301815291815281516020928301206000898152600f909352912054146127f55760405162461bcd60e51b815260206004820152601a602482015279135d5cdd081b585d18da08185c9d1a5cdd081c1c9bdc1bdcd85b60321b6044820152606401610e4f565b6000600e60008881526020019081526020016000209050858160020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550848160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550838160020160146101000a81548160ff021916908360ff160217905550828160010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550818160010160146101000a81548160ff021916908360ff1602179055506000801b600f600089815260200190815260200160002081905550867fc582d05e1da854143bd3271ef4529d79cf5a69fc6057ae320f357acfd291b73860405160405180910390a250505050505050565b6000828152600e602052604081206010548291829182918291829182918291908a9060649061294b90600160a01b900460ff1683615bd0565b61295591906159e0565b99506129618a82615be7565b60115490915060649061297e90600160a01b900460ff168d615bd0565b61298891906159e0565b97506129948882615be7565b60028301549091506064906129b390600160a01b900460ff1683615bd0565b6129bd91906159e0565b93506129c98482615be7565b6010546011546001600160a01b039182169b50169750955085156129f85760028201546001600160a01b031694505b8315612a0c5781546001600160a01b031692505b50509295985092959890939650565b6000828152600b60205260409020805460609190600160301b900462ffffff168310612a575750506040805160208101909152600081526113ba565b6000838152600982016020526040902054611b9d906001600160a01b03166147c4565b60006123616006546001600160a01b031690565b60608060608060606000600b60008881526020019081526020016000209050806001018054612abc906157ce565b80601f0160208091040260200160405190810160405280929190818152602001828054612ae8906157ce565b8015612b355780601f10612b0a57610100808354040283529160200191612b35565b820191906000526020600020905b815481529060010190602001808311612b1857829003601f168201915b50505050509550806002018054612b4b906157ce565b80601f0160208091040260200160405190810160405280929190818152602001828054612b77906157ce565b8015612bc45780601f10612b9957610100808354040283529160200191612bc4565b820191906000526020600020905b815481529060010190602001808311612ba757829003601f168201915b505050506003830154919650506001600160a01b031680612bf657604051806020016040528060008152509450612c02565b612bff816147c4565b94505b816004018054612c11906157ce565b80601f0160208091040260200160405190810160405280929190818152602001828054612c3d906157ce565b8015612c8a5780601f10612c5f57610100808354040283529160200191612c8a565b820191906000526020600020905b815481529060010190602001808311612c6d57829003601f168201915b50505050509350816005018054612ca0906157ce565b80601f0160208091040260200160405190810160405280929190818152602001828054612ccc906157ce565b8015612d195780601f10612cee57610100808354040283529160200191612d19565b820191906000526020600020905b815481529060010190602001808311612cfc57829003601f168201915b50505050509250505091939590929450565b606060018054611437906157ce565b612d4381613ff9565b6000818152600b6020526040808220805460ff60901b198116600160901b9182900460ff161590910217905551651c185d5cd95960d21b918391600080516020615f128339815191529190a350565b612d9d338383614adf565b5050565b612daa8261435f565b612dc557612dc033306351d9665360e11b611af9565b612de3565b6000828152600e60205260409020600201546001600160a01b031633145b612e445760405162461bcd60e51b815260206004820152602c60248201527f4f6e6c7920617274697374207768656e20756e6c6f636b65642c206f776e657260448201526b081dda195b881b1bd8dad95960a21b6064820152608401610e4f565b612e4d8161440a565b6000838152600b602052604080822060030180546001600160a01b0319166001600160a01b03949094169390931790925590516a3232b9b1b934b83a34b7b760a91b918491600080516020615f128339815191529190a35050565b60188054612eb5906157ce565b80601f0160208091040260200160405190810160405280929190818152602001828054612ee1906157ce565b8015612f2e5780601f10612f0357610100808354040283529160200191612f2e565b820191906000526020600020905b815481529060010190602001808311612f1157829003601f168201915b505050505081565b612f3f82614050565b612f5082632b2b404960e21b614098565b612f5981614310565b6000828152600b60205260409020612f708261440a565b8154600160301b9081900462ffffff9081166000908152600985016020526040902080546001600160a01b0319166001600160a01b0394909416939093179092558254612fc292919004166001615bad565b815462ffffff91909116600160301b0262ffffff60301b19909116178155604051651cd8dc9a5c1d60d21b908490600080516020615f1283398151915290600090a3505050565b613012826143a9565b6130238263ad576c4560e01b614098565b6000828152600b60205260409020600a0154610100900462ffffff1680821061305e5760405162461bcd60e51b8152600401610e4f90615963565b600061306b600183615bfa565b6000858152600b6020818152604080842062ffffff861685529092019052808220868352912091925090806130a08382615c16565b50600182810154828201805460ff90921692909160ff1916908360028111156130cb576130cb615302565b021790555060019182015491018054610100600160a81b031916610100928390046001600160a01b03169092029190911790556000848152600b6020818152604080842062ffffff8616855290920190528120906131298282614e77565b5060010180546001600160a81b03191690556000848152600b6020526040808220600a01805463ffffff00191661010062ffffff86160217905551849186917fa2290757ebb75dea327ace340daae74c1ac76aca04f6fd163fbf5b87e6d707599190a350505050565b60606123617a08ecadc82e4e86e646286dee4caac66be8adcced2dccabe8cd8caf602b1b6148c5565b6131c483614050565b6131d58363b1656ba360e01b614098565b6131de81614310565b6000838152600b602052604090208054600160301b900462ffffff1683106132405760405162461bcd60e51b81526020600482015260156024820152747363726970744964206f7574206f662072616e676560581b6044820152606401610e4f565b6132498261440a565b600084815260098301602052604080822080546001600160a01b0319166001600160a01b0394909416939093179092559051651cd8dc9a5c1d60d21b918691600080516020615f128339815191529190a350505050565b60095460009082106132ea5760405162461bcd60e51b8152602060048201526013602482015272496e646578206f7574206f6620626f756e647360681b6044820152606401610e4f565b600982815481106132fd576132fd61591e565b6000918252602090912001546001600160a01b031692915050565b61332182614050565b61333282635bd827d760e11b614098565b61333b81614310565b6000828152600b602052604090206002016133568282615865565b50604051696172746973744e616d6560b01b908390600080516020615f1283398151915290600090a35050565b600d8054612eb5906157ce565b61339a33836145ca565b6133b65760405162461bcd60e51b8152600401610e4f90615a11565b6112db84848484614ba9565b6133cb816143a9565b6133dc8163ba3c234560e01b614098565b6000818152600b6020526040808220600a01805460ff191660011790555182917fcbd4f9053ca1f820efad7731ae6c938613bfef183ee6f03f61cc8d4ab583f5dc91a250565b60608061342e83613e20565b60408051600480825260a082019092529060208201608080368337505060408051600480825260a08201909252929450905060208201608080368337019050509050600061347b84611a85565b6000818152600e6020526040812080546001820154939450909260ff600160a01b92839004811693929091041690826134b5836064615be7565b6134bf9190615bd0565b905060006134cd8484615bd0565b60135460155491925090600084156135475760028801548b516001600160a01b03909116908c90839081106135045761350461591e565b6001600160a01b0390921660209283029190910190910152848a826135288161594a565b93508151811061353a5761353a61591e565b6020026020010181815250505b83156135b55760018801548b516001600160a01b03909116908c90839081106135725761357261591e565b6001600160a01b0390921660209283029190910190910152838a826135968161594a565b9350815181106135a8576135a861591e565b6020026020010181815250505b8215613621576012548b516001600160a01b03909116908c90839081106135de576135de61591e565b6001600160a01b0390921660209283029190910190910152828a826136028161594a565b9350815181106136145761361461591e565b6020026020010181815250505b811561368d576014548b516001600160a01b03909116908c908390811061364a5761364a61591e565b6001600160a01b0390921660209283029190910190910152818a8261366e8161594a565b9350815181106136805761368061591e565b6020026020010181815250505b80600411156136a85780600403808c51038c52808b51038b52505b505050505050505050915091565b6136c663bd3d10e760e01b6141d5565b60646136d28284615ce2565b111561371b5760405162461bcd60e51b81526020600482015260186024820152774d61782073756d206f66204f4e455f48554e44524544202560401b6044820152606401610e4f565b6010805460ff808516600160a01b90810260ff60a01b19938416179093556011805491851690930291161790556040517970726f76696465725072696d61727950657263656e746167657360301b90600080516020615ef283398151915290600090a25050565b61378b82613ff9565b605f8111156137cf5760405162461bcd60e51b815260206004820152601060248201526f13dd995c881b585e081c195c98d95b9d60821b6044820152606401610e4f565b6000828152600e6020526040808220805460ff60a01b1916600160a01b60ff8616021790555170726f79616c747950657263656e7461676560781b918491600080516020615f128339815191529190a35050565b606061382e82613e20565b6000600b600061383d85611a85565b81526020019081526020016000206006018054613859906157ce565b80601f0160208091040260200160405190810160405280929190818152602001828054613885906157ce565b80156138d25780601f106138a7576101008083540402835291602001916138d2565b820191906000526020600020905b8154815290600101906020018083116138b557829003601f168201915b50505050509050806138e384614bdc565b6040516020016138f4929190615cf5565b604051602081830303815290604052915050919050565b61391b63cc90e72560e01b6141d5565b61392482614310565b61392d816144ba565b601754600160f81b900460ff16156139805760405162461bcd60e51b81526020600482015260166024820152752732bb90383937b532b1ba39903337b93134b23232b760511b6044820152606401610e4f565b6017546001600160f81b03166000818152600e6020908152604080832060020180546001600160a01b0319166001600160a01b038716179055600b90915290206001016139cd8482615865565b506000818152600b60205260409020805465ffffff00000060ff60901b011916613d09601e1b600160901b01178155600601613a0a601882615c16565b50613a16816001615d24565b601780546001600160f81b0319166001600160f81b03929092169190911790556040516618dc99585d195960ca1b908290600080516020615f1283398151915290600090a3505050565b613a7063340f0e4360e21b6141d5565b613a7981614843565b6000818152600b6020526040808220805460ff60881b198116600160881b9182900460ff1615909102179055516561637469766560d01b918391600080516020615f128339815191529190a350565b613ad8631aa1ea2760e31b6141d5565b600c613ae5828483615a92565b506000612387565b613afd636d90d42360e11b6141d5565b612710613b0a8284615ce2565b1115613b4e5760405162461bcd60e51b81526020600482015260136024820152724f766572206d61782073756d206f662042505360681b6044820152606401610e4f565b601382905560158190556040517370726f76696465725365636f6e6461727942505360601b90600080516020615ef283398151915290600090a25050565b613b9581614050565b613ba68163db2ff86160e01b614098565b6000818152600b602052604090208054600160301b900462ffffff16613c055760405162461bcd60e51b81526020600482015260146024820152734e6f207363726970747320746f2072656d6f766560601b6044820152606401610e4f565b80546009820190600090613c2790600190600160301b900462ffffff16615bfa565b62ffffff908116825260208201929092526040908101600090812080546001600160a01b0319169055835462ffffff60301b198116600160301b918290048516600019019094160292909217835551651cd8dc9a5c1d60d21b918491600080516020615f128339815191529190a35050565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6000818152600b60205260408120600781015460609283929091613cea906148c5565b9350806008018054613cfb906157ce565b80601f0160208091040260200160405190810160405280929190818152602001828054613d27906157ce565b8015613d745780601f10613d4957610100808354040283529160200191613d74565b820191906000526020600020905b815481529060010190602001808311613d5757829003601f168201915b5050935496989297505050600160301b90940462ffffff169392505050565b613d9b614a6e565b6001600160a01b038116613e005760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610e4f565b611af681614cdc565b600c8054612eb5906157ce565b6000612361612a7a565b613e2981614d07565b611af65760405162461bcd60e51b8152602060048201526017602482015276151bdad95b88125108191bd95cc81b9bdd08195e1a5cdd604a1b6044820152606401610e4f565b6000818152600b6020526040808220805467ffffffffffffffff60481b1916600160481b426001600160401b031602179055516818dbdb5c1b195d195960ba1b918391600080516020615f128339815191529190a350565b6001600160a01b038216613f1d5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610e4f565b613f2681614d07565b15613f725760405162461bcd60e51b815260206004820152601c60248201527b115490cdcc8c4e881d1bdad95b88185b1c9958591e481b5a5b9d195960221b6044820152606401610e4f565b6001600160a01b0382166000908152600360205260408120805460019290613f9b908490615ce2565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6000818152600e60205260409020600201546001600160a01b03163314611af65760405162461bcd60e51b815260206004820152600b60248201526a13db9b1e48185c9d1a5cdd60aa1b6044820152606401610e4f565b6140598161435f565b611af65760405162461bcd60e51b815260206004820152601060248201526f13db9b1e481a59881d5b9b1bd8dad95960821b6044820152606401610e4f565b6000828152600e60205260409020600201546001600160a01b03163314806140c657506140c6333083611af9565b612d9d5760405162461bcd60e51b815260206004820181905260248201527f4f6e6c7920617274697374206f722041646d696e2041434c20616c6c6f7765646044820152606401610e4f565b600080805b60208160ff161015614171576000868260ff166020811061413a5761413a61591e565b1a9050801580159061414f575060808160ff16105b15614168578560ff168160ff1603614168578260010192505b50600101614117565b8360ff168260ff1614925050509392505050565b60006001600160e01b031982166380ac58cd60e01b14806141b657506001600160e01b03198216635b5e139f60e01b145b806113ba57506301ffc9a760e01b6001600160e01b03198316146113ba565b6141e0333083611af9565b611af65760405162461bcd60e51b815260206004820152601660248201527513db9b1e4810591b5a5b881050d308185b1b1bddd95960521b6044820152606401610e4f565b601754600160f81b900460ff1661142657601780546001600160f81b0316600160f81b179055604051733732bba83937b532b1ba39a337b93134b23232b760611b90600080516020615ef283398151915290600090a2565b61428681614d07565b611af65760405162461bcd60e51b8152600401610e4f90615b7b565b600081815260046020526040902080546001600160a01b0319166001600160a01b03841690811790915581906142d782612426565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b8051600003611af65760405162461bcd60e51b815260206004820152601b60248201527a4d75737420696e707574206e6f6e2d656d70747920737472696e6760281b6044820152606401610e4f565b600061436a82614843565b6000828152600b6020526040902054600160481b90046001600160401b031680158080611b9d57506224ea006143a08342615be7565b10949350505050565b6000818152600b60205260409020600a015460ff1615611af65760405162461bcd60e51b815260206004820152601c60248201527b115e1d195c9b985b0819195c195b99195b98da595cc81b1bd8dad95960221b6044820152606401610e4f565b6000807f42797465636f646553746f726167655f56312e302e305f5f5f5f5f5f5f5f5f20308460405160200161444293929190615d44565b60405160208183030381529060405290508051602082016000f091506001600160a01b0382166144b45760405162461bcd60e51b815260206004820152601e60248201527f436f6e7472616374417353746f726167653a205772697465204572726f7200006044820152606401610e4f565b50919050565b6001600160a01b038116611af65760405162461bcd60e51b815260206004820152601b60248201527a4d75737420696e707574206e6f6e2d7a65726f206164647265737360281b6044820152606401610e4f565b601180546001600160a01b038085166001600160a01b031992831617909255601480548484169083161790556010805487841690831617905560128054928616929091169190911790556040517570726f766964657253616c657341646472657373657360501b90600080516020615ef283398151915290600090a250505050565b601861459c8282615865565b506040516d64656661756c744261736555524960901b90600080516020615ef283398151915290600090a250565b6000806145d683612426565b9050806001600160a01b0316846001600160a01b031614806145fd57506145fd8185613c99565b80611b9d5750836001600160a01b0316614616846114ba565b6001600160a01b031614949350505050565b826001600160a01b031661463b82612426565b6001600160a01b03161461469f5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610e4f565b6001600160a01b0382166147015760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610e4f565b61470c6000826142a2565b6001600160a01b0383166000908152600360205260408120805460019290614735908490615be7565b90915550506001600160a01b0382166000908152600360205260408120805460019290614763908490615ce2565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b604051630eacc5e760e31b815260609073f0585df582a0ad119f1616fb82f3b449a98eecd5906375662f38906147fe90859060040161502c565b600060405180830381865af415801561481b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113ba9190810190615da5565b7f0000000000000000000000000000000000000000000000000000000000000001811015801561487d57506017546001600160f81b031681105b611af65760405162461bcd60e51b8152602060048201526019602482015278141c9bda9958dd08125108191bd95cc81b9bdd08195e1a5cdd603a1b6044820152606401610e4f565b606060005b828160ff16602081106148df576148df61591e565b1a60f81b6001600160f81b031916158015906148fe575060208160ff16105b15614915578061490d81615e12565b9150506148ca565b60405191506040820160405280825282602083015250919050565b61493b333083611af9565b8061497a5750600061494b612a7a565b6001600160a01b031614801561497a57506000828152600e60205260409020600201546001600160a01b031633145b612d9d5760405162461bcd60e51b815260206004820152603860248201527f4f6e6c792041646d696e2041434c20616c6c6f7765642c206f722061727469736044820152771d081a59881bdddb995c881a185cc81c995b9bdd5b98d95960421b6064820152608401610e4f565b600880546001600160a01b0383166001600160a01b031991821681179092556009805460018101825560009182527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af01805490921690921790556040517072616e646f6d697a65724164647265737360781b91600080516020615ef283398151915291a250565b33614a77612a7a565b6001600160a01b0316146114265760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610e4f565b614ad5614a6e565b6114266000614cdc565b816001600160a01b0316836001600160a01b031603614b3c5760405162461bcd60e51b815260206004820152601960248201527822a9219b99189d1030b8383937bb32903a379031b0b63632b960391b6044820152606401610e4f565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b614bb4848484614628565b614bc084848484614d24565b6112db5760405162461bcd60e51b8152600401610e4f90615e31565b606081600003614c035750506040805180820190915260018152600360fc1b602082015290565b8160005b8115614c2d5780614c178161594a565b9150614c269050600a836159e0565b9150614c07565b6000816001600160401b03811115614c4757614c4761506c565b6040519080825280601f01601f191660200182016040528015614c71576020820181803683370190505b5090505b8415611b9d57614c86600183615be7565b9150614c93600a86615e83565b614c9e906030615ce2565b60f81b818381518110614cb357614cb361591e565b60200101906001600160f81b031916908160001a905350614cd5600a866159e0565b9450614c75565b614ce581614e25565b600a80546001600160a01b0319166001600160a01b0392909216919091179055565b6000908152600260205260409020546001600160a01b0316151590565b60006001600160a01b0384163b15614e1a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614d68903390899088908890600401615e97565b6020604051808303816000875af1925050508015614da3575060408051601f3d908101601f19168201909252614da091810190615ed4565b60015b614e00573d808015614dd1576040519150601f19603f3d011682016040523d82523d6000602084013e614dd6565b606091505b508051600003614df85760405162461bcd60e51b8152600401610e4f90615e31565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611b9d565b506001949350505050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b508054614e83906157ce565b6000825580601f10614e93575050565b601f016020900490600052602060002090810190611af691905b80821115614ec15760008155600101614ead565b5090565b60008060408385031215614ed857600080fd5b50508035926020909101359150565b6001600160a01b0381168114611af657600080fd5b600080600060608486031215614f1157600080fd5b8335614f1c81614ee7565b9250602084013591506040840135614f3381614ee7565b809150509250925092565b60008060408385031215614f5157600080fd5b82359150602083013562ffffff81168114614f6b57600080fd5b809150509250929050565b6001600160e01b031981168114611af657600080fd5b600060208284031215614f9e57600080fd5b8135614fa981614f76565b9392505050565b60005b83811015614fcb578181015183820152602001614fb3565b50506000910152565b60008151808452614fec816020860160208601614fb0565b601f01601f19169290920160200192915050565b602081526000614fa96020830184614fd4565b60006020828403121561502557600080fd5b5035919050565b6001600160a01b0391909116815260200190565b6000806040838503121561505357600080fd5b823561505e81614ee7565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156150aa576150aa61506c565b604052919050565b60006001600160401b038211156150cb576150cb61506c565b50601f01601f191660200190565b60006150ec6150e7846150b2565b615082565b905082815283838301111561510057600080fd5b828260208301376000602084830101529392505050565b600082601f83011261512857600080fd5b614fa9838335602085016150d9565b6000806040838503121561514a57600080fd5b8235915060208301356001600160401b0381111561516757600080fd5b61517385828601615117565b9150509250929050565b80356003811061518c57600080fd5b919050565b600080600080608085870312156151a757600080fd5b843593506020850135925060408501356001600160401b038111156151cb57600080fd5b6151d787828801615117565b9250506151e66060860161517d565b905092959194509250565b6000806000806080858703121561520757600080fd5b843561521281614ee7565b9350602085013561522281614ee7565b9250604085013561523281614ee7565b9150606085013561524281614ee7565b939692955090935050565b60006020828403121561525f57600080fd5b81356001600160401b0381111561527557600080fd5b611b9d84828501615117565b60008060006060848603121561529657600080fd5b83356152a181614ee7565b925060208401356152b181614ee7565b91506040840135614f3381614f76565b6000806000606084860312156152d657600080fd5b83356152e181614ee7565b925060208401356152f181614ee7565b929592945050506040919091013590565b634e487b7160e01b600052602160045260246000fd5b6003811061533657634e487b7160e01b600052602160045260246000fd5b9052565b60208152600082516080602084015261535660a0840182614fd4565b9050602084015161536a6040850182615318565b5060408401516001600160a01b0316606084810191909152840151838203601f1901608085015261539b8282614fd4565b95945050505050565b6000602082840312156153b657600080fd5b8135614fa981614ee7565b60008060008060008060c087890312156153da57600080fd5b8635955060208701356153ec81614ee7565b945060408701356153fc81614ee7565b935060608701359250608087013561541381614ee7565b8092505060a087013590509295509295509295565b6000806020838503121561543b57600080fd5b82356001600160401b038082111561545257600080fd5b818501915085601f83011261546657600080fd5b81358181111561547557600080fd5b86602082850101111561548757600080fd5b60209290920196919550909350505050565b6000806000606084860312156154ae57600080fd5b8335925060208401356001600160401b038111156154cb57600080fd5b6154d786828701615117565b9250506154e66040850161517d565b90509250925092565b6000806040838503121561550257600080fd5b823591506020830135614f6b81614ee7565b60a08152600061552760a0830188614fd4565b82810360208401526155398188614fd4565b9050828103604084015261554d8187614fd4565b905082810360608401526155618186614fd4565b905082810360808401526155758185614fd4565b98975050505050505050565b6001600160a01b031991909116815260200190565b8015158114611af657600080fd5b600080604083850312156155b757600080fd5b82356155c281614ee7565b91506020830135614f6b81615596565b6000806000606084860312156155e757600080fd5b833592506020840135915060408401356001600160401b0381111561560b57600080fd5b61561786828701615117565b9150509250925092565b6000806000806080858703121561563757600080fd5b843561564281614ee7565b9350602085013561565281614ee7565b92506040850135915060608501356001600160401b0381111561567457600080fd5b8501601f8101871361568557600080fd5b615694878235602084016150d9565b91505092959194509250565b604080825283519082018190526000906020906060840190828701845b828110156156e25781516001600160a01b0316845292840192908401906001016156bd565b5050508381038285015284518082528583019183019060005b81811015615717578351835292840192918401916001016156fb565b5090979650505050505050565b6000806040838503121561573757600080fd5b82356001600160401b0381111561574d57600080fd5b61575985828601615117565b9250506020830135614f6b81614ee7565b6000806040838503121561577d57600080fd5b823561578881614ee7565b91506020830135614f6b81614ee7565b6060815260006157ab6060830186614fd4565b82810360208401526157bd8186614fd4565b915050826040830152949350505050565b600181811c908216806157e257607f821691505b6020821081036144b457634e487b7160e01b600052602260045260246000fd5b601f8211156115f157600081815260208120601f850160051c810160208610156158295750805b601f850160051c820191505b8181101561584857828155600101615835565b505050505050565b600019600383901b1c191660019190911b1790565b81516001600160401b0381111561587e5761587e61506c565b6158928161588c84546157ce565b84615802565b602080601f8311600181146158c157600084156158af5750858301515b6158b98582615850565b865550615848565b600085815260208120601f198616915b828110156158f0578886015182559484019460019091019084016158d1565b508582101561590e5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820161595c5761595c615934565b5060010190565b602080825260189082015277417373657420696e646578206f7574206f662072616e676560401b604082015260600190565b6060815260006159a86060830186614fd4565b90506159b76020830185615318565b62ffffff83166040830152949350505050565b634e487b7160e01b600052601260045260246000fd5b6000826159ef576159ef6159ca565b500490565b600060208284031215615a0657600080fd5b8151614fa981615596565b6020808252602e908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526d1c881b9bdc88185c1c1c9bdd995960921b606082015260800190565b6001600160a01b039586168152938516602085015260408401929092529092166060820152608081019190915260a00190565b6001600160401b03831115615aa957615aa961506c565b615abd83615ab783546157ce565b83615802565b6000601f841160018114615aeb5760008515615ad95750838201355b615ae38682615850565b845550615b45565b600083815260209020601f19861690835b82811015615b1c5786850135825560209485019460019092019101615afc565b5086821015615b395760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b602080825260189082015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b604082015260600190565b62ffffff818116838216019080821115615bc957615bc9615934565b5092915050565b80820281158282048414176113ba576113ba615934565b818103818111156113ba576113ba615934565b62ffffff828116828216039080821115615bc957615bc9615934565b818103615c21575050565b615c2b82546157ce565b6001600160401b03811115615c4257615c4261506c565b615c508161588c84546157ce565b6000601f821160018114615c7e5760008315615c6c5750848201545b615c768482615850565b855550615b45565b600085815260209020601f19841690600086815260209020845b83811015615cb85782860154825560019586019590910190602001615c98565b508583101561590e5793015460001960f8600387901b161c19169092555050600190811b01905550565b808201808211156113ba576113ba615934565b60008351615d07818460208801614fb0565b835190830190615d1b818360208801614fb0565b01949350505050565b6001600160f81b03818116838216019080821115615bc957615bc9615934565b6a600b5981380380925939f360a81b8152607f60f91b600b820152600c81018490526000602c8201819052606084901b6001600160601b03191660388301528251615d9681604c850160208701614fb0565b91909101604c01949350505050565b600060208284031215615db757600080fd5b81516001600160401b03811115615dcd57600080fd5b8201601f81018413615dde57600080fd5b8051615dec6150e7826150b2565b818152856020838501011115615e0157600080fd5b61539b826020830160208601614fb0565b600060ff821660ff8103615e2857615e28615934565b60010192915050565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b600082615e9257615e926159ca565b500690565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615eca90830184614fd4565b9695505050505050565b600060208284031215615ee657600080fd5b8151614fa981614f7656fe8b810f233ce7ee6e962ab4d98bf0277751de1f5589de3dcc812ac2047994d009b96a30340e86d03ce4be42f94ac02d7b27b4a4cdae942beb69026718dfe66afca2646970667358221220959ce413ab227fb70b80f103d89ff1c764d4211f38f32e4ada8042c9305e84b864736f6c63430008130033

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.