ETH Price: $2,508.44 (-0.02%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw Artist ...225971472025-05-30 17:49:238 days ago1748627363IN
0xDeA98CB7...52Ed7a4e3
0 ETH0.000406774.64385712
Admin Artist Aut...225971422025-05-30 17:48:238 days ago1748627303IN
0xDeA98CB7...52Ed7a4e3
0 ETH0.00445494.89929386
Top Up Bid225971182025-05-30 17:43:358 days ago1748627015IN
0xDeA98CB7...52Ed7a4e3
0.15 ETH0.000663186.27424652
Top Up Bid225970952025-05-30 17:38:598 days ago1748626739IN
0xDeA98CB7...52Ed7a4e3
0.0125 ETH0.000434515.77753929
Create Bid225970942025-05-30 17:38:478 days ago1748626727IN
0xDeA98CB7...52Ed7a4e3
1.2125 ETH0.000854015.82123444
Top Up Bid225970912025-05-30 17:38:118 days ago1748626691IN
0xDeA98CB7...52Ed7a4e3
0.0125 ETH0.000403435.36334816
Top Up Bid225970882025-05-30 17:37:358 days ago1748626655IN
0xDeA98CB7...52Ed7a4e3
0.3375 ETH0.000760266.6202388
Top Up Bid225970812025-05-30 17:36:118 days ago1748626571IN
0xDeA98CB7...52Ed7a4e3
0.325 ETH0.000455926.06318765
Create Bid225970762025-05-30 17:35:118 days ago1748626511IN
0xDeA98CB7...52Ed7a4e3
1.5 ETH0.000682076.09689045
Create Bid225970712025-05-30 17:34:118 days ago1748626451IN
0xDeA98CB7...52Ed7a4e3
1.1875 ETH0.000864245.89101471
Top Up Bid225970712025-05-30 17:34:118 days ago1748626451IN
0xDeA98CB7...52Ed7a4e3
0.2 ETH0.000619595.86191471
Top Up Bid225970552025-05-30 17:30:598 days ago1748626259IN
0xDeA98CB7...52Ed7a4e3
0.0375 ETH0.000519116.96116689
Top Up Bid225970532025-05-30 17:30:358 days ago1748626235IN
0xDeA98CB7...52Ed7a4e3
0.025 ETH0.000442785.8885234
Top Up Bid225970512025-05-30 17:30:118 days ago1748626211IN
0xDeA98CB7...52Ed7a4e3
0.0375 ETH0.00063835.55818181
Create Bid225970472025-05-30 17:29:238 days ago1748626163IN
0xDeA98CB7...52Ed7a4e3
1.5 ETH0.00089366.09115993
Top Up Bid225970442025-05-30 17:28:478 days ago1748626127IN
0xDeA98CB7...52Ed7a4e3
0.05 ETH0.000669466.11909291
Top Up Bid225970362025-05-30 17:27:118 days ago1748626031IN
0xDeA98CB7...52Ed7a4e3
0.0125 ETH0.000505236.71667736
Create Bid225970302025-05-30 17:25:598 days ago1748625959IN
0xDeA98CB7...52Ed7a4e3
1.15 ETH0.000963626.56841496
Create Bid225970212025-05-30 17:24:118 days ago1748625851IN
0xDeA98CB7...52Ed7a4e3
1.1375 ETH0.000722516.25881113
Top Up Bid225970172025-05-30 17:23:238 days ago1748625803IN
0xDeA98CB7...52Ed7a4e3
0.0375 ETH0.000494766.57745123
Top Up Bid225970112025-05-30 17:22:118 days ago1748625731IN
0xDeA98CB7...52Ed7a4e3
0.8625 ETH0.000750086.53226253
Create Bid225970052025-05-30 17:20:598 days ago1748625659IN
0xDeA98CB7...52Ed7a4e3
1.1375 ETH0.000799766.92800396
Top Up Bid225970032025-05-30 17:20:358 days ago1748625635IN
0xDeA98CB7...52Ed7a4e3
0.0375 ETH0.0007016.10346751
Create Bid225969972025-05-30 17:19:238 days ago1748625563IN
0xDeA98CB7...52Ed7a4e3
1.125 ETH0.000937466.39011596
Top Up Bid225969902025-05-30 17:17:598 days ago1748625479IN
0xDeA98CB7...52Ed7a4e3
0.0875 ETH0.000521046.89511412
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer225971472025-05-30 17:49:238 days ago1748627363
0xDeA98CB7...52Ed7a4e3
11.75625 ETH
Transfer225971472025-05-30 17:49:238 days ago1748627363
0xDeA98CB7...52Ed7a4e3
1.30625 ETH
Transfer225971422025-05-30 17:48:238 days ago1748627303
0xDeA98CB7...52Ed7a4e3
0.025 ETH
Transfer225971422025-05-30 17:48:238 days ago1748627303
0xDeA98CB7...52Ed7a4e3
0.3125 ETH
Transfer225971422025-05-30 17:48:238 days ago1748627303
0xDeA98CB7...52Ed7a4e3
0.325 ETH
Transfer225971422025-05-30 17:48:238 days ago1748627303
0xDeA98CB7...52Ed7a4e3
0.325 ETH
Transfer225971422025-05-30 17:48:238 days ago1748627303
0xDeA98CB7...52Ed7a4e3
0.325 ETH
Transfer225971422025-05-30 17:48:238 days ago1748627303
0xDeA98CB7...52Ed7a4e3
0.8125 ETH
Transfer225971422025-05-30 17:48:238 days ago1748627303
0xDeA98CB7...52Ed7a4e3
5.1125 ETH
Transfer225971422025-05-30 17:48:238 days ago1748627303
0xDeA98CB7...52Ed7a4e3
5.1625 ETH
Transfer225971422025-05-30 17:48:238 days ago1748627303
0xDeA98CB7...52Ed7a4e3
6.0125 ETH
Transfer225971422025-05-30 17:48:238 days ago1748627303
0xDeA98CB7...52Ed7a4e3
6.1125 ETH
Transfer225970942025-05-30 17:38:478 days ago1748626727
0xDeA98CB7...52Ed7a4e3
1.175 ETH
Transfer225970762025-05-30 17:35:118 days ago1748626511
0xDeA98CB7...52Ed7a4e3
1.1625 ETH
Transfer225970712025-05-30 17:34:118 days ago1748626451
0xDeA98CB7...52Ed7a4e3
1.15 ETH
Transfer225970472025-05-30 17:29:238 days ago1748626163
0xDeA98CB7...52Ed7a4e3
1.125 ETH
Transfer225970302025-05-30 17:25:598 days ago1748625959
0xDeA98CB7...52Ed7a4e3
1.1 ETH
Transfer225970212025-05-30 17:24:118 days ago1748625851
0xDeA98CB7...52Ed7a4e3
1.1 ETH
Transfer225970052025-05-30 17:20:598 days ago1748625659
0xDeA98CB7...52Ed7a4e3
1.1 ETH
Transfer225969972025-05-30 17:19:238 days ago1748625563
0xDeA98CB7...52Ed7a4e3
1.0875 ETH
Transfer225969852025-05-30 17:16:598 days ago1748625419
0xDeA98CB7...52Ed7a4e3
0.8 ETH
Transfer225969692025-05-30 17:13:358 days ago1748625215
0xDeA98CB7...52Ed7a4e3
0.7 ETH
Transfer225969462025-05-30 17:08:598 days ago1748624939
0xDeA98CB7...52Ed7a4e3
0.66875 ETH
Transfer225969292025-05-30 17:05:358 days ago1748624735
0xDeA98CB7...52Ed7a4e3
0.66875 ETH
Transfer225969242025-05-30 17:04:358 days ago1748624675
0xDeA98CB7...52Ed7a4e3
0.65 ETH
View All Internal Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MinterRAMV0

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 10 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

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

import {ISharedMinterV0} from "../../interfaces/v0.8.x/ISharedMinterV0.sol";
import {ISharedMinterRAMV0} from "../../interfaces/v0.8.x/ISharedMinterRAMV0.sol";
import {IMinterFilterV1} from "../../interfaces/v0.8.x/IMinterFilterV1.sol";

import {ABHelpers} from "../../libs/v0.8.x/ABHelpers.sol";
import {AuthLib} from "../../libs/v0.8.x/AuthLib.sol";
import {RAMLib} from "../../libs/v0.8.x/minter-libs/RAMLib.sol";
import {SplitFundsLib} from "../../libs/v0.8.x/minter-libs/SplitFundsLib.sol";
import {MaxInvocationsLib} from "../../libs/v0.8.x/minter-libs/MaxInvocationsLib.sol";

import {ReentrancyGuard} from "@openzeppelin-4.7/contracts/security/ReentrancyGuard.sol";
import {SafeCast} from "@openzeppelin-4.7/contracts/utils/math/SafeCast.sol";

/**
 * @title Filtered Minter contract that allows tokens to be minted with ETH.
 * Pricing is achieved using a fully on-chain ranked auction mechanism.
 * This is designed to be used with GenArt721CoreContractV3 flagship or
 * engine contracts.
 * @author Art Blocks Inc.
 * @notice Bid Front-Running:
 * Collectors can front-run bids, potentially causing bid transactions to
 * revert. The minter attempts to handle this in a simple and transparent
 * manner, but some transactions reverting, especially during highly
 * competitive auctions, is unavoidable, and best remedied by resubmitting a
 * new bid transaction with a higher value.
 * @notice Privileged Roles and Ownership:
 * This contract is designed to be managed, with limited powers.
 * Privileged roles and abilities are controlled by the core contract's Admin
 * ACL contract a project's artist, and auction winners. The Admin ACL and
 * project's artist roles hold extensive power and can modify minter details.
 * Care must be taken to ensure that the admin ACL contract and artist
 * addresses are secure behind a multi-sig or other access control mechanism.
 *
 * Additional admin and artist privileged roles may be described on other
 * contracts that this minter integrates with.
 * @notice Fallback and Error States:
 * This minter implements protections for collectors and artists and admin with
 * the intention of preventing any single party from being able to deny
 * revenue or minting rights to another party, for any time longer than
 * 72 hours. All funds are held non-custodially by the smart contract until
 * all tokens are minted or bids are refunded, after which they may be
 * distributed to the artist and admin. All settlements are non-custodial and
 * may be claimed by the winning bidder at any time after an auction ends.
 * ----------------------------------------------------------------------------
 * @notice Project-Minter STATE, FLAG, and ERROR Summary
 * Note: STATEs are mutually exclusive and are in-order, State C potentially skipped
 * -------------
 * STATE A: Pre-Auction
 * abilities:
 *  - (artist) configure project max invocations
 *  - (artist) configure project auction
 * -------------
 * STATE B: Live-Auction
 * abilities:
 *  - (minter active) create bid
 *  - (minter active) top-up bid
 *  - (admin) emergency increase auction end time by up to 72 hr (in cases of frontend downtime, etc.)
 *  - (artist)(not in extra time)(no previous admin extension) reduce auction length
 * -------------
 * STATE C: Post-Auction, Admin-Artist Mint Period (if applicable)
 * abilities:
 *  - (admin | artist) auto-mint tokens to winners
 *  - (winner) collect settlement
 *  - (ERROR E1)(admin) auto-refund winning bids that cannot receive tokens due to max invocations error
 *  note: State C is skipped if auction was not a sellout
 * -------------
 * STATE D: Post-Auction, Open Mint Period
 * abilities:
 *  - (winner | admin | artist) directly mint tokens to winners, any order
 *  - (winner) collect settlement
 *  - (FLAG F1) purchase remaining tokens for auction min price (base price), like fixed price minter
 *  - (ERROR E1)(winner | admin | artist) directly refund bids due to max invocations error state, any order
 * -------------
 * STATE E: Post-Auction, all bids handled
 * note: "all bids handled" guarantees not in ERROR E1
 *  - (artist | admin) collect revenues
 *  - (FLAG F1) purchase remaining tokens for auction min price (base price), like fixed price minter
 * -------------
 * FLAGS
 * F1: tokens owed < invocations available
 *     occurs when an auction ends before selling out, so tokens are available to be purchased
 *     note: also occurs during Pre and Live auction, so FLAG F1 can occur with STATE A, B, but should not enable purchases
 * -------------
 * ERRORS
 * E1: tokens owed > invocations available
 *     occurs when tokens minted on different minter or core max invocations were reduced after auction bidding began.
 *     indicates operational error occurred.
 *     resolution: when winning bids have refunded to sufficiently reduce tokens owed == invocations available.
 *     note: error state does not affect minimum winning bid price, and therefore does not affect settlement amount due to any
 *     winning bids.
 * ----------------------------------------------------------------------------
 * @notice Caution: While Engine projects must be registered on the Art Blocks
 * Core Registry to assign this minter, this minter does not enforce that a
 * project is registered when configured or queried. This is primarily for gas
 * optimization purposes. It is, therefore, possible that fake projects may be
 * configured on this minter, but bids will not be able to be placed due to
 * checks performed by this minter's Minter Filter.
 *
 * @dev Note that while this minter makes use of `block.timestamp` and it is
 * technically possible that this value is manipulated by block producers, such
 * manipulation will not have material impact on the ability for collectors to
 * place a bid before auction end time. Minimum limits are set on time
 * intervals such that this manipulation would not have a material impact on
 * the auction process.
 */
contract MinterRAMV0 is ReentrancyGuard, ISharedMinterV0, ISharedMinterRAMV0 {
    using SafeCast for uint256;

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

    /// @notice Minter filter this minter may interact with.
    IMinterFilterV1 private immutable _minterFilter;

    /// @notice minterType for this minter
    string public constant minterType = "MinterRAMV0";

    /// @notice minter version for this minter
    string public constant minterVersion = "v0.0.0";

    /// @notice Minimum auction duration
    uint256 public constant MIN_AUCTION_DURATION_SECONDS = 60 * 10; // 10 minutes

    /** @notice Gas limit for refunding ETH to bidders
     * configurable by admin, default to 30,000
     * max uint24 ~= 16 million gas, more than enough for a refund
     * @dev SENDALL fallback is used to refund ETH if this limit is exceeded
     */
    uint24 internal _minterRefundGasLimit = 30_000;

    /**
     * @notice Initializes contract to be a shared, filtered minter for
     * minter filter `minterFilter`
     * @param minterFilter Minter filter for which this will be a minter
     */
    constructor(address minterFilter) ReentrancyGuard() {
        minterFilterAddress = minterFilter;
        _minterFilter = IMinterFilterV1(minterFilter);
        // emit events indicating default minter configuration values
        emit RAMLib.MinAuctionDurationSecondsUpdated({
            minAuctionDurationSeconds: MIN_AUCTION_DURATION_SECONDS
        });
        emit RAMLib.MinterRefundGasLimitUpdated({
            refundGasLimit: _minterRefundGasLimit
        });
        emit RAMLib.AuctionBufferTimeParamsUpdated({
            auctionBufferSeconds: RAMLib.AUCTION_BUFFER_SECONDS,
            maxAuctionExtraSeconds: RAMLib.MAX_AUCTION_EXTRA_SECONDS
        });
        emit RAMLib.NumSlotsUpdated({numSlots: RAMLib.NUM_SLOTS});
    }

    /**
     * @notice Sets the gas limit during ETH refunds when a collector is
     * outbid. This value should be set to a value that is high enough to
     * ensure that refunds are successful for commonly used wallets, but low
     * enough to avoid excessive abuse of refund gas allowance during a new
     * bid.
     * @dev max gas limit is ~16M, which is considered well over a future-safe
     * upper bound.
     * @param minterRefundGasLimit Gas limit to set for refunds. Must be
     * between 7,000 and max uint24 (~16M).
     */
    function updateRefundGasLimit(uint24 minterRefundGasLimit) external {
        // CHECKS
        AuthLib.onlyMinterFilterAdminACL({
            minterFilterAddress: minterFilterAddress,
            sender: msg.sender,
            contract_: address(this),
            selector: this.updateRefundGasLimit.selector
        });
        // @dev max gas limit implicitly checked by using uint24 input arg
        // @dev min gas limit is based on rounding up current cost to send ETH
        // to a Gnosis Safe wallet, which accesses cold address and emits event
        require(minterRefundGasLimit >= 7_000, "Only gte 7_000");
        // EFFECTS
        _minterRefundGasLimit = minterRefundGasLimit;
        emit RAMLib.MinterRefundGasLimitUpdated(minterRefundGasLimit);
    }

    /**
     * @notice Contract-Admin only function to update the requirements on if a
     * post-auction admin-artist-only mint period is required or banned, for
     * and-on configured projects.
     * @param coreContract core contract to set the configuration for.
     * @param adminMintingConstraint enum indicating if the minter should
     * require an admin-artist-only mint period after the auction ends or not.
     */
    function setContractConfig(
        address coreContract,
        RAMLib.AdminMintingConstraint adminMintingConstraint
    ) external {
        // CHECKS
        AuthLib.onlyCoreAdminACL({
            coreContract: coreContract,
            sender: msg.sender,
            contract_: address(this),
            selector: this.setContractConfig.selector
        });
        // EFFECTS
        RAMLib.setContractConfig({
            coreContract: coreContract,
            adminMintingConstraint: adminMintingConstraint
        });
    }

    /**
     * @notice Contract-Admin only function to add emergency auction hours to
     * auction of project `projectId` on core contract `coreContract`.
     * Protects against unexpected frontend downtime, etc.
     * Reverts if called by anyone other than a contract admin.
     * Reverts if project is not in a Live Auction.
     * Reverts if auction is already in extra time.
     * Reverts if adding more than the maximum number of emergency hours.
     * @param projectId Project ID to add emergency auction hours to.
     * @param coreContract Core contract address for the given project.
     * @param emergencyHoursToAdd Number of emergency hours to add to the
     * project's auction.
     */
    function adminAddEmergencyAuctionHours(
        uint256 projectId,
        address coreContract,
        uint8 emergencyHoursToAdd
    ) external nonReentrant {
        // CHECKS
        AuthLib.onlyCoreAdminACL({
            coreContract: coreContract,
            sender: msg.sender,
            contract_: address(this),
            selector: this.adminAddEmergencyAuctionHours.selector
        });
        // EFFECTS
        RAMLib.adminAddEmergencyAuctionHours({
            projectId: projectId,
            coreContract: coreContract,
            emergencyHoursToAdd: emergencyHoursToAdd
        });
    }

    /**
     * @notice Manually sets the local maximum invocations of project `projectId`
     * with the provided `maxInvocations`, checking that `maxInvocations` is less
     * than or equal to the value of project `project_id`'s maximum invocations that is
     * set on the core contract.
     * @dev Note that a `maxInvocations` of 0 can only be set if the current `invocations`
     * value is also 0 and this would also set `maxHasBeenInvoked` to true, correctly short-circuiting
     * this minter's purchase function, avoiding extra gas costs from the core contract's maxInvocations check.
     * @param projectId Project ID to set the maximum invocations for.
     * @param coreContract Core contract address for the given project.
     * @param maxInvocations Maximum invocations to set for the project.
     */
    function manuallyLimitProjectMaxInvocations(
        uint256 projectId,
        address coreContract,
        uint24 maxInvocations
    ) external {
        // CHECKS
        AuthLib.onlyArtist({
            projectId: projectId,
            coreContract: coreContract,
            sender: msg.sender
        });
        // only project minter state A (Pre-Auction)
        RAMLib.ProjectMinterStates currentState = RAMLib.getProjectMinterState({
            projectId: projectId,
            coreContract: coreContract
        });
        require(
            currentState == RAMLib.ProjectMinterStates.PreAuction,
            "Only pre-auction"
        );
        // EFFECTS
        MaxInvocationsLib.manuallyLimitProjectMaxInvocations({
            projectId: projectId,
            coreContract: coreContract,
            maxInvocations: maxInvocations
        });
        // also update number of tokens in auction
        RAMLib.refreshNumTokensInAuction({
            projectId: projectId,
            coreContract: coreContract
        });
    }

    /**
     * @notice Sets auction details for project `projectId`.
     * @param projectId Project ID to set auction details for.
     * @param coreContract Core contract address for the given project.
     * @param auctionTimestampStart Timestamp at which to start the auction.
     * @param basePrice Resting price of the auction, in Wei.
     * @param allowExtraTime Boolean indicating if extra time is allowed for
     * the auction, when valid bids are placed near the end of the auction.
     * @param adminArtistOnlyMintPeriodIfSellout Boolean indicating if an
     * admin-artist-only mint period should be enforced if the auction sells
     * out.
     * @dev Note that a basePrice of `0` will cause the transaction to revert.
     */
    function setAuctionDetails(
        uint256 projectId,
        address coreContract,
        uint40 auctionTimestampStart,
        uint40 auctionTimestampEnd,
        uint256 basePrice,
        bool allowExtraTime,
        bool adminArtistOnlyMintPeriodIfSellout
    ) external nonReentrant {
        AuthLib.onlyArtist({
            projectId: projectId,
            coreContract: coreContract,
            sender: msg.sender
        });
        // CHECKS
        // check min auction duration
        // @dev underflow checked automatically in solidity 0.8
        require(
            auctionTimestampEnd - auctionTimestampStart >=
                MIN_AUCTION_DURATION_SECONDS,
            "Auction too short"
        );
        // EFFECTS
        // @dev project minter state checked in setAuctionDetails
        RAMLib.setAuctionDetails({
            projectId: projectId,
            coreContract: coreContract,
            auctionTimestampStart: auctionTimestampStart,
            auctionTimestampEnd: auctionTimestampEnd,
            basePrice: basePrice.toUint88(),
            allowExtraTime: allowExtraTime,
            adminArtistOnlyMintPeriodIfSellout: adminArtistOnlyMintPeriodIfSellout
        });
    }

    /**
     * @notice Reduces the auction length for project `projectId` on core
     * contract `coreContract` to `auctionTimestampEnd`.
     * Only allowed to be called during a live auction, and protects against
     * the case of an accidental excessively long auction, which locks funds.
     * Reverts if called by anyone other than the project's artist.
     * Reverts if project is not in a Live Auction.
     * Reverts if auction is not being reduced in length.
     * Reverts if in extra time.
     * Reverts if `auctionTimestampEnd` results in auction that is not at least
     * `MIN_AUCTION_DURATION_SECONDS` in duration.
     * Reverts if admin previously applied a time extension.
     * @param projectId Project ID to reduce the auction length for.
     * @param coreContract Core contract address for the given project.
     * @param auctionTimestampEnd New timestamp at which to end the auction.
     */
    function reduceAuctionLength(
        uint256 projectId,
        address coreContract,
        uint40 auctionTimestampEnd
    ) external nonReentrant {
        AuthLib.onlyArtist({
            projectId: projectId,
            coreContract: coreContract,
            sender: msg.sender
        });
        RAMLib.reduceAuctionLength({
            projectId: projectId,
            coreContract: coreContract,
            auctionTimestampEnd: auctionTimestampEnd,
            minimumAuctionDurationSeconds: MIN_AUCTION_DURATION_SECONDS
        });
    }

    /**
     * @notice Places a bid for project `projectId` on core contract
     * `coreContract`.
     * Reverts if minter is not the active minter for projectId on minter
     * filter.
     * Reverts if project is not in a Live Auction.
     * Reverts if msg.value is not equal to slot value.
     * In order to successfully place the bid, the token bid must be:
     * - greater than or equal to a project's minimum bid price if maximum
     *   number of bids has not been reached
     * - sufficiently greater than the current minimum bid if maximum number
     *   of bids has been reached
     * If the bid is unsuccessful, the transaction will revert.
     * If the bid is successful, but outbid by another bid before the auction
     * ends, the funds will be noncustodially returned to the bidder's address,
     * `msg.sender`. A fallback method of sending funds back to the bidder via
     * SELFDESTRUCT (SENDALL) prevents denial of service attacks, even if the
     * original bidder reverts or runs out of gas during receive or fallback.
     * ------------------------------------------------------------------------
     * WARNING: bidders must be prepared to handle the case where their bid is
     * outbid and their funds are returned to the original `msg.sender` address
     * via SELFDESTRUCT (SENDALL).
     * ------------------------------------------------------------------------
     * @param projectId projectId being bid on.
     * @param coreContract Core contract address for the given project.
     * @param slotIndex Slot index to create the bid for.
     * @dev nonReentrant modifier is used to prevent reentrancy attacks, e.g.
     * an an auto-bidder that would be able to automically outbid a user's
     * new bid via a reentrant call to createBid.
     */
    function createBid(
        uint256 projectId,
        address coreContract,
        uint16 slotIndex
    ) external payable nonReentrant {
        // CHECKS
        // minter must be set for project on MinterFilter
        require(
            _minterFilter.getMinterForProject({
                projectId: projectId,
                coreContract: coreContract
            }) == address(this),
            "Minter not active"
        );
        // @dev bid value is checked against slot value in placeBid
        // @dev project state is checked in placeBid

        // EFFECTS
        RAMLib.placeBid({
            projectId: projectId,
            coreContract: coreContract,
            slotIndex: slotIndex,
            bidder: msg.sender,
            bidValue: msg.value,
            minterRefundGasLimit: _minterRefundGasLimit
        });
    }

    /**
     * @notice Top up bid for project `projectId` on core contract
     * `coreContract` for bid `bidId` to new slot index `newSlotIndex`.
     * Reverts if Bid ID has been kicked out of the auction or does not exist.
     * Reverts if msg.sender is not the bidder of the bid.
     * Reverts if minter is not the active minter for projectId on minter
     * filter.
     * Reverts if project is not in a Live Auction.
     * Reverts if msg.value is not equal to difference in bid values between
     * new and old slots.
     * Reverts if new slot index is not greater than or equal to the current
     * slot index.
     * @param projectId Project ID to top up bid for.
     * @param coreContract Core contract address for the given project.
     * @param bidId ID of bid to top up.
     * @param newSlotIndex New slot index to move bid to.
     */
    function topUpBid(
        uint256 projectId,
        address coreContract,
        uint32 bidId,
        uint16 newSlotIndex
    ) external payable nonReentrant {
        // CHECKS
        // minter must be set for project on MinterFilter
        require(
            _minterFilter.getMinterForProject({
                projectId: projectId,
                coreContract: coreContract
            }) == address(this),
            "Minter not active"
        );
        // @dev additional bid value is checked against slot value in topUpBid
        // @dev project state is checked in topUpBid

        // EFFECTS
        RAMLib.topUpBid({
            projectId: projectId,
            coreContract: coreContract,
            bidId: bidId,
            newSlotIndex: newSlotIndex,
            bidder: msg.sender,
            addedValue: msg.value
        });
    }

    /**
     * @notice Purchases token for project `projectId` on core contract
     * `coreContract` for auction that has ended, but not yet been sold out.
     * @param projectId Project ID to purchase token for.
     * @param coreContract Core contract address for the given project.
     * @return tokenId Token ID of minted token
     */
    function purchase(
        uint256 projectId,
        address coreContract
    ) external payable nonReentrant returns (uint256 tokenId) {
        // @dev checks performed in RAMLib purchaseTo function
        tokenId = RAMLib.purchaseTo({
            to: msg.sender,
            projectId: projectId,
            coreContract: coreContract,
            minterFilter: _minterFilter
        });
    }

    /**
     * @notice Purchases token for project `projectId` on core contract
     * `coreContract` for auction that has ended, but not yet been sold out,
     * and sets the token's owner to `to`.
     * @param to Address to be the new token's owner.
     * @param projectId Project ID to purchase token for.
     * @param coreContract Core contract address for the given project.
     * @return tokenId Token ID of minted token
     */
    function purchaseTo(
        address to,
        uint256 projectId,
        address coreContract
    ) external payable nonReentrant returns (uint256 tokenId) {
        // @dev checks performed in RAMLib purchaseTo function
        tokenId = RAMLib.purchaseTo({
            to: to,
            projectId: projectId,
            coreContract: coreContract,
            minterFilter: _minterFilter
        });
    }

    /**
     * @notice Collects settlement for project `projectId` on core contract
     * `coreContract` for all bids in `bidIds`,
     * which must be aligned by index.
     * Reverts if msg.sender is not the bidder for all bids.
     * Reverts if project is not in a post-auction state.
     * Reverts if one or more bids has already been settled.
     * Reverts if invalid bid is found.
     * @param projectId Project ID of bid to collect settlement for
     * @param coreContract Core contract address for the given project.
     * @param bidIds IDs of bids to collect settlements for
     */
    function collectSettlements(
        uint256 projectId,
        address coreContract,
        uint32[] calldata bidIds
    ) external nonReentrant {
        // CHECKS
        // @dev project state is checked in collectSettlements
        // @dev length of slotIndices and bidIndicesInSlot must be equal is
        // checked in collectSettlements
        // EFFECTS
        RAMLib.collectSettlements({
            projectId: projectId,
            coreContract: coreContract,
            bidIds: bidIds,
            bidder: msg.sender,
            minterRefundGasLimit: _minterRefundGasLimit
        });
    }

    /**
     * @notice Contract-Admin or Artist only function to mint tokens to winners
     * of project `projectId` on core contract `coreContract`.
     * Automatically mints tokens to most-winning bids, in order from highest
     * and earliest bid to lowest and latest bid.
     * Settles bids as tokens are minted, if not already settled.
     * Reverts if project is not in a post-auction state, admin-artist-only
     * mint period (i.e. State C), with tokens available.
     * Reverts if msg.sender is not a contract admin or artist.
     * Reverts if number of tokens to mint is greater than the number of
     * tokens available to be minted.
     * @param projectId Project ID to mint tokens on.
     * @param coreContract Core contract address for the given project.
     * @param numTokensToMint Number of tokens to mint in this transaction.
     */
    function adminArtistAutoMintTokensToWinners(
        uint256 projectId,
        address coreContract,
        uint24 numTokensToMint
    ) external nonReentrant {
        // CHECKS
        AuthLib.onlyCoreAdminACLOrArtist({
            projectId: projectId,
            coreContract: coreContract,
            sender: msg.sender,
            contract_: address(this),
            selector: this.adminArtistAutoMintTokensToWinners.selector
        });
        // EFFECTS/INTERACTIONS
        RAMLib.adminArtistAutoMintTokensToWinners({
            projectId: projectId,
            coreContract: coreContract,
            numTokensToMint: numTokensToMint,
            minterFilter: _minterFilter,
            minterRefundGasLimit: _minterRefundGasLimit
        });
    }

    /**
     * @notice Directly mint tokens to winners of project `projectId` on core
     * contract `coreContract`.
     * Does not guarantee an optimal ordering or handling of E1 state like
     * `adminAutoRefundWinners` does while in State C.
     * Admin or Artist may mint to any winning bids.
     * Provides protection for Admin and Artist because they may mint tokens
     * to winners to prevent denial of revenue claiming.
     * Skips over bids that have already been minted or refunded (front-running
     * protection).
     * Reverts if project is not in a post-auction state,
     * post-admin-artist-only mint period (i.e. State D), with tokens available
     * Reverts if msg.sender is not a contract admin or artist.
     * @param projectId Project ID to mint tokens on.
     * @param coreContract Core contract address for the given project.
     * @param bidIds IDs of bids to mint tokens for
     */
    function adminArtistDirectMintTokensToWinners(
        uint256 projectId,
        address coreContract,
        uint32[] calldata bidIds
    ) external nonReentrant {
        // CHECKS
        AuthLib.onlyCoreAdminACLOrArtist({
            projectId: projectId,
            coreContract: coreContract,
            sender: msg.sender,
            contract_: address(this),
            selector: this.adminArtistDirectMintTokensToWinners.selector
        });
        // EFFECTS/INTERACTIONS
        RAMLib.directMintTokensToWinners({
            projectId: projectId,
            coreContract: coreContract,
            bidIds: bidIds,
            requireSenderIsBidder: false, // not required when called by admin or artist
            minterFilter: _minterFilter,
            minterRefundGasLimit: _minterRefundGasLimit
        });
    }

    /**
     * @notice Directly mint tokens of winner of project `projectId` on core
     * contract `coreContract`.
     * Does not guarantee an optimal ordering or handling of E1 state like
     * `adminAutoRefundWinners` does while in State C.
     * Only winning collector may call and mint tokens to themselves.
     * Provides protection for collectors because they may mint their tokens
     * directly.
     * Skips over bids that have already been minted or refunded (front-running
     * protection)
     * Reverts if project is not in a post-auction state,
     * post-admin-artist-only mint period (i.e. State D), with tokens available
     * Reverts if msg.sender is not the winning bidder for all specified bids.
     * @param projectId Project ID to mint tokens on.
     * @param coreContract Core contract address for the given project.
     * @param bidIds IDs of bids to mint tokens for
     */
    function winnerDirectMintTokens(
        uint256 projectId,
        address coreContract,
        uint32[] calldata bidIds
    ) external nonReentrant {
        // CHECKS
        // @dev all checks performed in library function
        // EFFECTS/INTERACTIONS
        RAMLib.directMintTokensToWinners({
            projectId: projectId,
            coreContract: coreContract,
            bidIds: bidIds,
            requireSenderIsBidder: true, // only allow winning bidder to call
            minterFilter: _minterFilter,
            minterRefundGasLimit: _minterRefundGasLimit
        });
    }

    /**
     * @notice Directly refund bids for project `projectId` on core
     * contract `coreContract` to resolve error state E1.
     * Does not guarantee an optimal ordering or handling of E1 state like
     * `adminAutoMintTokensToWinners` does while in State C.
     * Admin or Artist may refund to any bids.
     * Provides protection for Admin and Artist because they may refund to
     * resolve E1 state to prevent denial of revenue claiming.
     * Skips over bids that have already been minted or refunded (front-running
     * protection).
     * Reverts if project is not in a post-auction state,
     * post-admin-artist-only mint period (i.e. State D).
     * Reverts if project is not in error state E1.
     * Reverts if length of bids to refund exceeds the number of bids that need
     * to be refunded to resolve the error state E1.
     * Reverts if bid does not exist at bidId.
     * Reverts if msg.sender is not a contract admin or artist.
     * @param projectId Project ID to refund bid values on.
     * @param coreContract Core contract address for the given project.
     * @param bidIds IDs of bids to refund bid values for
     */
    function adminArtistDirectRefundWinners(
        uint256 projectId,
        address coreContract,
        uint32[] calldata bidIds
    ) external nonReentrant {
        // CHECKS
        AuthLib.onlyCoreAdminACLOrArtist({
            projectId: projectId,
            coreContract: coreContract,
            sender: msg.sender,
            contract_: address(this),
            selector: this.adminArtistDirectRefundWinners.selector
        });
        // EFFECTS/INTERACTIONS
        RAMLib.directRefundBidsToResolveE1({
            projectId: projectId,
            coreContract: coreContract,
            bidIds: bidIds,
            requireSenderIsBidder: false, // not required when called by admin or artist
            minterRefundGasLimit: _minterRefundGasLimit
        });
    }

    /**
     * @notice Directly refund bids for project `projectId` on core
     * contract `coreContract` to resolve error state E1.
     * Does not guarantee an optimal ordering or handling of E1 state like
     * `adminAutoMintTokensToWinners` does while in State C.
     * Only winning collector may call and refund to themselves.
     * Provides protection for collectors because they may refund their tokens
     * directly if in E1 state and they are no longer able to mint their
     * token(s) (prevent holding of funds).
     * Skips over bids that have already been minted or refunded (front-running
     * protection).
     * Reverts if project is not in a post-auction state,
     * post-admin-artist-only mint period (i.e. State D).
     * Reverts if project is not in error state E1.
     * Reverts if length of bids to refund exceeds the number of bids that need
     * to be refunded to resolve the error state E1.
     * Reverts if msg.sender is not the winning bidder for all specified bids.
     * @param projectId Project ID to refund bid values on.
     * @param coreContract Core contract address for the given project.
     * @param bidIds IDs of bids to refund bid values for
     */
    function winnerDirectRefund(
        uint256 projectId,
        address coreContract,
        uint32[] calldata bidIds
    ) external nonReentrant {
        // CHECKS
        // @dev all checks performed in library function
        // EFFECTS/INTERACTIONS
        RAMLib.directRefundBidsToResolveE1({
            projectId: projectId,
            coreContract: coreContract,
            bidIds: bidIds,
            requireSenderIsBidder: true, // only allow winning bidder to call
            minterRefundGasLimit: _minterRefundGasLimit
        });
    }

    /**
     * @notice Function to automatically refund the lowest winning bids for
     * project `projectId` on core contract `coreContract` to resolve error
     * state E1.
     * Reverts if not called by a contract admin.
     * Reverts if project is not in post-auction state C.
     * Reverts if project is not in error state E1.
     * Reverts if numBidsToRefund exceeds the number of bids that need to be
     * refunded to resolve the error state E1.
     * @dev Admin-only requirement is not for security, but is to enable Admin
     * to be aware that an error state has been encountered while in post-
     * auction state C.
     * @param projectId Project ID to refunds bids for.
     * @param coreContract Core contract address for the given project.
     * @param numBidsToRefund Number of bids to refund in this call.
     */
    function adminAutoRefundWinners(
        uint256 projectId,
        address coreContract,
        uint24 numBidsToRefund
    ) external nonReentrant {
        // CHECKS
        AuthLib.onlyCoreAdminACL({
            coreContract: coreContract,
            sender: msg.sender,
            contract_: address(this),
            selector: this.adminAutoRefundWinners.selector
        });
        // EFFECTS/INTERACTIONS
        RAMLib.autoRefundBidsToResolveE1({
            projectId: projectId,
            coreContract: coreContract,
            numBidsToRefund: numBidsToRefund,
            minterRefundGasLimit: _minterRefundGasLimit
        });
    }

    /**
     * @notice This withdraws project revenues for project `projectId` on core
     * contract `coreContract` to the artist and admin, only after all bids
     * have been minted+settled or refunded.
     * Note that the conditions described are the equivalent of project minter
     * State E.
     * @param projectId Project ID to withdraw revenues for.
     * @param coreContract Core contract address for the given project.
     */
    function withdrawArtistAndAdminRevenues(
        uint256 projectId,
        address coreContract
    ) external nonReentrant {
        // CHECKS
        AuthLib.onlyCoreAdminACLOrArtist({
            projectId: projectId,
            coreContract: coreContract,
            sender: msg.sender,
            contract_: address(this),
            selector: this.withdrawArtistAndAdminRevenues.selector
        });
        // EFFECTS/INTERACTIONS
        RAMLib.withdrawArtistAndAdminRevenues({
            projectId: projectId,
            coreContract: coreContract
        });
    }

    /**
     * @notice Returns if project minter is in ERROR state E1, and the number
     * of bids that need to be refunded to resolve the error.
     * E1: Tokens owed > invocations available
     * Occurs when: tokens are minted on different minter after auction begins,
     * or when core contract max invocations are reduced after auction begins.
     * Resolution: Admin must refund the lowest bids after auction ends.
     * @param projectId Project Id to query
     * @param coreContract Core contract address to query
     * @return isError True if in error state, false otherwise
     * @return numBidsToRefund Number of bids to refund to resolve error, 0 if
     * not in error state
     */
    function getIsErrorE1(
        uint256 projectId,
        address coreContract
    ) external view returns (bool isError, uint256 numBidsToRefund) {
        (isError, numBidsToRefund, ) = RAMLib.isErrorE1FlagF1({
            projectId: projectId,
            coreContract: coreContract
        });
    }

    /**
     * @notice View function to return the current minter-level configuration
     * details. Some or all of these values may be defined as constants for
     * this minter.
     * @return minAuctionDurationSeconds Minimum auction duration in seconds
     * @return auctionBufferSeconds Auction buffer time in seconds
     * @return maxAuctionExtraSeconds Maximum extra time in seconds
     * @return maxAuctionAdminEmergencyExtensionHours Maximum emergency
     * extension hours for admin
     * @return adminArtistOnlyMintTimeSeconds Admin-artist-only mint time in
     * seconds
     * @return minterRefundGasLimit Gas limit for refunding ETH
     */
    function minterConfigurationDetails()
        external
        view
        returns (
            uint256 minAuctionDurationSeconds,
            uint256 auctionBufferSeconds,
            uint256 maxAuctionExtraSeconds,
            uint256 maxAuctionAdminEmergencyExtensionHours,
            uint256 adminArtistOnlyMintTimeSeconds,
            uint24 minterRefundGasLimit
        )
    {
        minAuctionDurationSeconds = MIN_AUCTION_DURATION_SECONDS;
        auctionBufferSeconds = RAMLib.AUCTION_BUFFER_SECONDS;
        maxAuctionExtraSeconds = RAMLib.MAX_AUCTION_EXTRA_SECONDS;
        maxAuctionAdminEmergencyExtensionHours = RAMLib
            .MAX_AUCTION_ADMIN_EMERGENCY_EXTENSION_HOURS;
        adminArtistOnlyMintTimeSeconds = RAMLib
            .ADMIN_ARTIST_ONLY_MINT_TIME_SECONDS;
        minterRefundGasLimit = _minterRefundGasLimit;
    }

    /**
     * @notice Gets the admin minting constraint configuration details for a core contract as configured by a
     * contract admin, for this minter.
     * @param coreContract The address of the core contract.
     * @return RAMLib.AdminMintingConstraint enum value.
     */
    function contractConfigurationDetails(
        address coreContract
    ) external view returns (RAMLib.AdminMintingConstraint) {
        return RAMLib.getRAMAdminMintingConstraintValue(coreContract);
    }

    /**
     * @notice Gets the maximum invocations project configuration.
     * @dev RAMLib shims in logic to properly return maxHasBeenInvoked based
     * on project state, bid state, and core contract state.
     * @param projectId The ID of the project whose data needs to be fetched.
     * @param coreContract The address of the core contract.
     * @return MaxInvocationsLib.MaxInvocationsProjectConfig instance with the
     * configuration data.
     */
    function maxInvocationsProjectConfig(
        uint256 projectId,
        address coreContract
    )
        external
        view
        returns (MaxInvocationsLib.MaxInvocationsProjectConfig memory)
    {
        // RAM minter does not update maxHasBeenInvoked, so we ask the RAMLib
        // for this state, and it shims in an appropriate maxHasBeenInvoked
        // value based on the state of the auction, unminted bids, core
        // contract invocations, and minter max invocations
        return
            RAMLib.getMaxInvocationsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
    }

    /**
     * @notice Returns the auction details for project `projectId` on core
     * contract `coreContract`.
     * @param projectId is an existing project ID.
     * @param coreContract is an existing core contract address.
     * @return auctionTimestampStart is the timestamp at which the auction
     * starts.
     * @return auctionTimestampEnd is the timestamp at which the auction ends.
     * @return basePrice is the resting price of the auction, in Wei.
     * @return numTokensInAuction is the number of tokens in the auction.
     * @return numBids is the number of bids in the auction.
     * @return numBidsMintedTokens is the number of bids that have been minted
     * into tokens.
     * @return numBidsErrorRefunded is the number of bids that have been
     * refunded due to an error state.
     * @return minBidSlotIndex is the index of the slot with the minimum bid
     * value.
     * @return allowExtraTime is a bool indicating if the auction is allowed to
     * have extra time.
     * @return adminArtistOnlyMintPeriodIfSellout is a bool indicating if an
     * admin-artist-only mint period is required if the auction sells out.
     * @return revenuesCollected is a bool indicating if the auction revenues
     * have been collected.
     * @return projectMinterState is the current state of the project minter.
     * @dev projectMinterState is a RAMLib.ProjectMinterStates enum value.
     */
    function getAuctionDetails(
        uint256 projectId,
        address coreContract
    )
        external
        view
        returns (
            uint256 auctionTimestampStart,
            uint256 auctionTimestampEnd,
            uint256 basePrice,
            uint256 numTokensInAuction,
            uint256 numBids,
            uint256 numBidsMintedTokens,
            uint256 numBidsErrorRefunded,
            uint256 minBidSlotIndex,
            bool allowExtraTime,
            bool adminArtistOnlyMintPeriodIfSellout,
            bool revenuesCollected,
            RAMLib.ProjectMinterStates projectMinterState
        )
    {
        return
            RAMLib.getAuctionDetails({
                projectId: projectId,
                coreContract: coreContract
            });
    }

    /**
     * @notice Returns if project has reached maximum number of invocations for
     * a given project and core contract, properly accounting for the auction
     * state, unminted bids, core contract invocations, and minter max
     * invocations when determining maxHasBeenInvoked
     * @param projectId is an existing project ID.
     * @param coreContract is an existing core contract address.
     */
    function projectMaxHasBeenInvoked(
        uint256 projectId,
        address coreContract
    ) external view returns (bool) {
        return
            RAMLib.getMaxHasBeenInvoked({
                projectId: projectId,
                coreContract: coreContract
            });
    }

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

    /**
     * @notice Checks if the specified `coreContract` is a valid engine contract.
     * @dev This function retrieves the cached value of `isEngine` from
     * the `isEngineCache` mapping. If the cached value is already set, it
     * returns the cached value. Otherwise, it calls the `getV3CoreIsEngineView`
     * function from the `SplitFundsLib` library to check if `coreContract`
     * is a valid engine contract.
     * @dev This function will revert if the provided `coreContract` is not
     * a valid Engine or V3 Flagship contract.
     * @param coreContract The address of the contract to check.
     * @return True if `coreContract` is a valid engine contract.
     */
    function isEngineView(address coreContract) external view returns (bool) {
        SplitFundsLib.IsEngineCache storage isEngineCache = SplitFundsLib
            .getIsEngineCacheConfig(coreContract);
        if (isEngineCache.isCached) {
            return isEngineCache.isEngine;
        } else {
            // @dev this calls the non-state-modifying variant of isEngine
            return SplitFundsLib.getV3CoreIsEngineView(coreContract);
        }
    }

    /**
     * @notice Gets minimum bid value to participate in an
     * auction for project `projectId` on core contract `coreContract`.
     * If an auction is not configured, `isConfigured` will be false, and a
     * dummy price of zero is assigned to `tokenPriceInWei`.
     * If an auction is configured but still in a pre-auction state,
     * `isConfigured` will be true, and `tokenPriceInWei` will be the minimum
     * initial bid price for the next token auction.
     * If there is an active auction, `isConfigured` will be true, and
     * `tokenPriceInWei` will be the current minimum bid's value + min bid
     * increment due to the minter's increment percentage, rounded up to next
     * slot's bid value.
     * If there is an auction that has ended (no longer accepting bids), but
     * the project is configured, `isConfigured` will be true, and
     * `tokenPriceInWei` will be either the sellout price or the reserve price
     * of the auction if it did not sell out during its auction.
     * Also returns currency symbol and address to be being used as payment,
     * which for this minter is ETH only.
     * @param projectId Project ID to get price information for.
     * @param coreContract Core contract to get price information for.
     * @return isConfigured true only if project auctions are configured.
     * @return tokenPriceInWei price in wei to become a bidder on a
     * token auction.
     * @return currencySymbol currency symbol for purchases of project on this
     * minter. This minter always returns "ETH"
     * @return currencyAddress currency address for purchases of project on
     * this minter. This minter always returns null address, reserved for ether
     */
    function getPriceInfo(
        uint256 projectId,
        address coreContract
    )
        external
        view
        returns (
            bool isConfigured,
            uint256 tokenPriceInWei,
            string memory currencySymbol,
            address currencyAddress
        )
    {
        (isConfigured, tokenPriceInWei) = RAMLib.getPriceInfo({
            projectId: projectId,
            coreContract: coreContract
        });
        // currency is always ETH
        currencySymbol = "ETH";
        currencyAddress = address(0);
    }

    /**
     * @notice Gets minimum next bid value in Wei and slot index for project `projectId`
     * on core contract `coreContract`.
     * If in a pre-auction state, reverts if unconfigured, otherwise returns
     * the minimum initial bid price for the upcoming auction.
     * If in an active auction, returns the minimum next bid's value and slot
     * index.
     * If in a post-auction state, reverts if auction was a sellout, otherwise
     * returns the auction's reserve price and slot index 0 (because tokens may
     * still be purchasable at the reserve price).
     * @param projectId Project ID to get the minimum next bid value for
     * @param coreContract Core contract address for the given project
     * @return minNextBidValueInWei minimum next bid value in Wei
     * @return minNextBidSlotIndex slot index of the minimum next bid
     */
    function getMinimumNextBid(
        uint256 projectId,
        address coreContract
    )
        external
        view
        returns (uint256 minNextBidValueInWei, uint256 minNextBidSlotIndex)
    {
        (minNextBidValueInWei, minNextBidSlotIndex) = RAMLib.getMinimumNextBid({
            projectId: projectId,
            coreContract: coreContract
        });
    }

    /**
     * @notice Returns the value of the lowest bid in the project's auction,
     * in Wei.
     * Reverts if no bids exist in the auction.
     * @param projectId Project ID to get the lowest bid value for
     * @param coreContract Core contract address for the given project
     * @return minBidValue Value of the lowest bid in the auction, in Wei
     */
    function getLowestBidValue(
        uint256 projectId,
        address coreContract
    ) external view returns (uint256) {
        (, uint16 minBidSlotIndex) = RAMLib.getLowestBid({
            projectId: projectId,
            coreContract: coreContract
        });
        // translate slot index to bid value
        uint88 projectBasePrice = RAMLib
            .getRAMProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            })
            .basePrice;
        return
            RAMLib.slotIndexToBidValue({
                basePrice: projectBasePrice,
                slotIndex: minBidSlotIndex
            });
    }

    /**
     * @notice Convenience view function that returns the bid value associated
     * with a given slot index for the specified project's auction, in Wei.
     * Reverts if the slot index is out of range (greater than 511).
     * @param projectId Project ID to get the bid value for
     * @param coreContract Core contract address for the given project
     * @param slotIndex Slot index to get the bid value for
     */
    function slotIndexToBidValue(
        uint256 projectId,
        address coreContract,
        uint16 slotIndex
    ) external view returns (uint256) {
        uint88 projectBasePrice = RAMLib
            .getRAMProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            })
            .basePrice;
        // @dev does not check if project configured to reduce bytecode size
        return
            RAMLib.slotIndexToBidValue({
                basePrice: projectBasePrice,
                slotIndex: slotIndex
            });
    }

    /**
     * @notice Returns balance of project `projectId` on core contract
     * `coreContract` on this minter contract.
     * @dev project balance is a failsafe backstop used to ensure that funds
     * from one project may never affect funds from another project on this
     * shared minter contract.
     * @param projectId Project ID to get the balance for
     * @param coreContract Core contract address for the given project
     */
    function getProjectBalance(
        uint256 projectId,
        address coreContract
    ) external view returns (uint256) {
        return
            RAMLib.getProjectBalance({
                projectId: projectId,
                coreContract: coreContract
            });
    }

    /**
     * @notice Exists for interface conformance only.
     * Use manuallyLimitProjectMaxInvocations to set the maximum invocations
     * for a project instead.
     */
    function syncProjectMaxInvocationsToCore(
        uint256 /*projectId*/,
        address /*coreContract*/
    ) public pure {
        revert("Action not supported");
    }
}

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

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
        // good first aproximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 1;
        }

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

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

File 5 of 33 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v2.5._
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v2.5._
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v2.5._
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v2.5._
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248) {
        require(value >= type(int248).min && value <= type(int248).max, "SafeCast: value doesn't fit in 248 bits");
        return int248(value);
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240) {
        require(value >= type(int240).min && value <= type(int240).max, "SafeCast: value doesn't fit in 240 bits");
        return int240(value);
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232) {
        require(value >= type(int232).min && value <= type(int232).max, "SafeCast: value doesn't fit in 232 bits");
        return int232(value);
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224) {
        require(value >= type(int224).min && value <= type(int224).max, "SafeCast: value doesn't fit in 224 bits");
        return int224(value);
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216) {
        require(value >= type(int216).min && value <= type(int216).max, "SafeCast: value doesn't fit in 216 bits");
        return int216(value);
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208) {
        require(value >= type(int208).min && value <= type(int208).max, "SafeCast: value doesn't fit in 208 bits");
        return int208(value);
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200) {
        require(value >= type(int200).min && value <= type(int200).max, "SafeCast: value doesn't fit in 200 bits");
        return int200(value);
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192) {
        require(value >= type(int192).min && value <= type(int192).max, "SafeCast: value doesn't fit in 192 bits");
        return int192(value);
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184) {
        require(value >= type(int184).min && value <= type(int184).max, "SafeCast: value doesn't fit in 184 bits");
        return int184(value);
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176) {
        require(value >= type(int176).min && value <= type(int176).max, "SafeCast: value doesn't fit in 176 bits");
        return int176(value);
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168) {
        require(value >= type(int168).min && value <= type(int168).max, "SafeCast: value doesn't fit in 168 bits");
        return int168(value);
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160) {
        require(value >= type(int160).min && value <= type(int160).max, "SafeCast: value doesn't fit in 160 bits");
        return int160(value);
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152) {
        require(value >= type(int152).min && value <= type(int152).max, "SafeCast: value doesn't fit in 152 bits");
        return int152(value);
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144) {
        require(value >= type(int144).min && value <= type(int144).max, "SafeCast: value doesn't fit in 144 bits");
        return int144(value);
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136) {
        require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits");
        return int136(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120) {
        require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits");
        return int120(value);
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112) {
        require(value >= type(int112).min && value <= type(int112).max, "SafeCast: value doesn't fit in 112 bits");
        return int112(value);
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104) {
        require(value >= type(int104).min && value <= type(int104).max, "SafeCast: value doesn't fit in 104 bits");
        return int104(value);
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96) {
        require(value >= type(int96).min && value <= type(int96).max, "SafeCast: value doesn't fit in 96 bits");
        return int96(value);
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88) {
        require(value >= type(int88).min && value <= type(int88).max, "SafeCast: value doesn't fit in 88 bits");
        return int88(value);
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80) {
        require(value >= type(int80).min && value <= type(int80).max, "SafeCast: value doesn't fit in 80 bits");
        return int80(value);
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72) {
        require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits");
        return int72(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56) {
        require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits");
        return int56(value);
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48) {
        require(value >= type(int48).min && value <= type(int48).max, "SafeCast: value doesn't fit in 48 bits");
        return int48(value);
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40) {
        require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits");
        return int40(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24) {
        require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits");
        return int24(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../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: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * 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 address zero.
     *
     * 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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

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

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

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

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

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 9 of 33 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }
}

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

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;

import "./IEngineRegistryV0.sol";

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

    function unregisterContracts(address[] calldata contractAddresses) external;

    function getNumRegisteredContracts() external view returns (uint256);

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

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

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;

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

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

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

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

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

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

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

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

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

    function genArt721CoreAddress() external returns (address);

    function minterFilterAddress() external returns (address);

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

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

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

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

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

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

import "./IFilteredMinterV0.sol";

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

import "./IFilteredMinterV1.sol";

pragma solidity ^0.8.0;

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

    // Sets the local max invocations for a given project, checking that the provided max invocations is
    // less than or equal to the global max invocations for the project set on the core contract.
    // This does not impact the max invocations value defined on the core contract.
    function manuallyLimitProjectMaxInvocations(
        uint256 _projectId,
        uint256 _maxInvocations
    ) external;
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

import "./IAdminACLV0.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 {
    // This interface 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.

    /**
     * @notice Event emitted when the Art Blocks Curation Registry contract is updated.
     * @dev only utilized by subset of V3 core contracts (Art Blocks Curated contracts)
     * @param artblocksCurationRegistryAddress Address of Art Blocks Curation Registry contract.
     */
    event ArtBlocksCurationRegistryContractUpdated(
        address indexed artblocksCurationRegistryAddress
    );

    /**
     * @notice Project's royalty splitter was updated to `_splitter`.
     * @dev New event in v3.2
     * @param projectId The project ID.
     * @param royaltySplitter The new splitter address to receive royalties.
     */
    event ProjectRoyaltySplitterUpdated(
        uint256 indexed projectId,
        address indexed royaltySplitter
    );

    // The following fields are used to indicate which contract-level parameter
    // has been updated in the `PlatformUpdated` event:
    // @dev only append to the end of this enum in the case of future updates
    enum PlatformUpdatedFields {
        FIELD_NEXT_PROJECT_ID, // 0
        FIELD_NEW_PROJECTS_FORBIDDEN, // 1
        FIELD_DEFAULT_BASE_URI, // 2
        FIELD_RANDOMIZER_ADDRESS, // 3
        FIELD_NEXT_CORE_CONTRACT, // 4
        FIELD_ARTBLOCKS_DEPENDENCY_REGISTRY_ADDRESS, // 5
        FIELD_ARTBLOCKS_ON_CHAIN_GENERATOR_ADDRESS, // 6
        FIELD_PROVIDER_SALES_ADDRESSES, // 7
        FIELD_PROVIDER_PRIMARY_SALES_PERCENTAGES, // 8
        FIELD_PROVIDER_SECONDARY_SALES_BPS, // 9
        FIELD_SPLIT_PROVIDER, // 10
        FIELD_BYTECODE_STORAGE_READER // 11
    }

    // The following fields are used to indicate which project-level parameter
    // has been updated in the `ProjectUpdated` event:
    // @dev only append to the end of this enum in the case of future updates
    enum ProjectUpdatedFields {
        FIELD_PROJECT_COMPLETED, // 0
        FIELD_PROJECT_ACTIVE, // 1
        FIELD_PROJECT_ARTIST_ADDRESS, // 2
        FIELD_PROJECT_PAUSED, // 3
        FIELD_PROJECT_CREATED, // 4
        FIELD_PROJECT_NAME, // 5
        FIELD_PROJECT_ARTIST_NAME, // 6
        FIELD_PROJECT_SECONDARY_MARKET_ROYALTY_PERCENTAGE, // 7
        FIELD_PROJECT_DESCRIPTION, // 8
        FIELD_PROJECT_WEBSITE, // 9
        FIELD_PROJECT_LICENSE, // 10
        FIELD_PROJECT_MAX_INVOCATIONS, // 11
        FIELD_PROJECT_SCRIPT, // 12
        FIELD_PROJECT_SCRIPT_TYPE, // 13
        FIELD_PROJECT_ASPECT_RATIO, // 14
        FIELD_PROJECT_BASE_URI, // 15
        FIELD_PROJECT_PROVIDER_SECONDARY_FINANCIALS // 16
    }

    /**
     * @notice Error codes for the GenArt721 contract. Used by the GenArt721Error
     * custom error.
     * @dev only append to the end of this enum in the case of future updates
     */
    enum ErrorCodes {
        OnlyNonZeroAddress, // 0
        OnlyNonEmptyString, // 1
        OnlyNonEmptyBytes, // 2
        TokenDoesNotExist, // 3
        ProjectDoesNotExist, // 4
        OnlyUnlockedProjects, // 5
        OnlyAdminACL, // 6
        OnlyArtist, // 7
        OnlyArtistOrAdminACL, // 8
        OnlyAdminACLOrRenouncedArtist, // 9
        OnlyMinterContract, // 10
        MaxInvocationsReached, // 11
        ProjectMustExistAndBeActive, // 12
        PurchasesPaused, // 13
        OnlyRandomizer, // 14
        TokenHashAlreadySet, // 15
        NoZeroHashSeed, // 16
        OverMaxSumOfPercentages, // 17
        IndexOutOfBounds, // 18
        OverMaxSumOfBPS, // 19
        MaxOf100Percent, // 20
        PrimaryPayeeIsZeroAddress, // 21
        SecondaryPayeeIsZeroAddress, // 22
        MustMatchArtistProposal, // 23
        NewProjectsForbidden, // 24
        NewProjectsAlreadyForbidden, // 25
        OnlyArtistOrAdminIfLocked, // 26
        OverMaxSecondaryRoyaltyPercentage, // 27
        OnlyMaxInvocationsDecrease, // 28
        OnlyGteInvocations, // 29
        ScriptIdOutOfRange, // 30
        NoScriptsToRemove, // 31
        ScriptTypeAndVersionFormat, // 32
        AspectRatioTooLong, // 33
        AspectRatioNoNumbers, // 34
        AspectRatioImproperFormat, // 35
        OnlyNullPlatformProvider, // 36
        ContractInitialized // 37
    }

    /**
     * @notice Emits an error code `_errorCode` in the GenArt721Error event.
     * @dev Emitting error codes instead of error strings saves significant
     * contract bytecode size, allowing for more contract functionality within
     * the 24KB contract size limit.
     * @param _errorCode The error code to emit. See ErrorCodes enum.
     */
    error GenArt721Error(ErrorCodes _errorCode);

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

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

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

/**
 * @notice Struct representing Engine contract configuration.
 * @param tokenName Name of token.
 * @param tokenSymbol Token symbol.
 * @param renderProviderAddress address to send render provider revenue to
 * @param randomizerContract Randomizer contract.
 * @param splitProviderAddress Address to use as royalty splitter provider for the contract.
 * @param minterFilterAddress Address of the Minter Filter to set as the Minter
 * on the contract.
 * @param startingProjectId The initial next project ID.
 * @param autoApproveArtistSplitProposals Whether or not to always
 * auto-approve proposed artist split updates.
 * @param nullPlatformProvider Enforce always setting zero platform provider fees and addresses.
 * @param allowArtistProjectActivation Allow artist to activate their own projects.
 * @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`.
 */
struct EngineConfiguration {
    string tokenName;
    string tokenSymbol;
    address renderProviderAddress;
    address platformProviderAddress;
    address newSuperAdminAddress;
    address randomizerContract;
    address splitProviderAddress;
    address minterFilterAddress;
    uint248 startingProjectId;
    bool autoApproveArtistSplitProposals;
    bool nullPlatformProvider;
    bool allowArtistProjectActivation;
}

interface IGenArt721CoreContractV3_Engine is IGenArt721CoreContractV3_Base {
    // @dev new function in V3.2
    /**
     * @notice Initializes the contract with the provided `engineConfiguration`.
     * This function should be called atomically, immediately after deployment.
     * Only callable once. Validation on `engineConfiguration` is performed by caller.
     * @param engineConfiguration EngineConfiguration to configure the contract with.
     * @param adminACLContract_ Address of admin access control contract, to be
     * set as contract owner.
     * @param defaultBaseURIHost Base URI prefix to initialize default base URI with.
     * @param bytecodeStorageReaderContract_ Address of the bytecode storage reader contract.
     */
    function initialize(
        EngineConfiguration calldata engineConfiguration,
        address adminACLContract_,
        string memory defaultBaseURIHost,
        address bytecodeStorageReaderContract_
    ) external;

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

    /** @notice The default render provider payment address for all secondary sales royalty
     * revenues, for all new projects. Individual project payment info is defined
     * in each project's ProjectFinance struct.
     * @return The default render provider payment address for secondary sales royalties.
     */
    function defaultRenderProviderSecondarySalesAddress()
        external
        view
        returns (address payable);

    /** @notice The default platform provider payment address for all secondary sales royalty
     * revenues, for all new projects. Individual project payment info is defined
     * in each project's ProjectFinance struct.
     * @return The default platform provider payment address for secondary sales royalties.
     */
    function defaultPlatformProviderSecondarySalesAddress()
        external
        view
        returns (address payable);

    /** @notice The default render provider payment basis points for all secondary sales royalty
     * revenues, for all new projects. Individual project payment info is defined
     * in each project's ProjectFinance struct.
     * @return The default render provider payment basis points for secondary sales royalties.
     */
    function defaultRenderProviderSecondarySalesBPS()
        external
        view
        returns (uint256);

    /** @notice The default platform provider payment basis points for all secondary sales royalty
     * revenues, for all new projects. Individual project payment info is defined
     * in each project's ProjectFinance struct.
     * @return The default platform provider payment basis points for secondary sales royalties.
     */
    function defaultPlatformProviderSecondarySalesBPS()
        external
        view
        returns (uint256);

    /**
     * @notice The address of the current split provider being used by the contract.
     * @return The address of the current split provider.
     */
    function splitProvider() external view returns (ISplitProviderV0);
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

import "./IFilteredMinterV2.sol";

pragma solidity ^0.8.0;

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

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

    function removeMinterForProject(
        uint256 projectId,
        address coreContract
    ) external;

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

    function updateCoreRegistry(address coreRegistry) external;

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc. to support the 0xSplits V2 integration
// Sourced from:
//  - https://github.com/0xSplits/splits-contracts-monorepo/blob/main/packages/splits-v2/src/libraries/SplitV2.sol
//  - https://github.com/0xSplits/splits-contracts-monorepo/blob/main/packages/splits-v2/src/splitters/SplitFactoryV2.sol

pragma solidity ^0.8.0;

interface ISplitFactoryV2 {
    /* -------------------------------------------------------------------------- */
    /*                                   STRUCTS                                  */
    /* -------------------------------------------------------------------------- */

    /**
     * @notice Split struct
     * @dev This struct is used to store the split information.
     * @dev There are no hard caps on the number of recipients/totalAllocation/allocation unit. Thus the chain and its
     * gas limits will dictate these hard caps. Please double check if the split you are creating can be distributed on
     * the chain.
     * @param recipients The recipients of the split.
     * @param allocations The allocations of the split.
     * @param totalAllocation The total allocation of the split.
     * @param distributionIncentive The incentive for distribution. Limits max incentive to 6.5%.
     */
    struct Split {
        address[] recipients;
        uint256[] allocations;
        uint256 totalAllocation;
        uint16 distributionIncentive;
    }

    /* -------------------------------------------------------------------------- */
    /*                                 FUNCTIONS                                  */
    /* -------------------------------------------------------------------------- */

    /**
     * @notice Create a new split with params and owner.
     * @param _splitParams Params to create split with.
     * @param _owner Owner of created split.
     * @param _creator Creator of created split.
     * @param _salt Salt for create2.
     * @return split Address of the created split.
     */
    function createSplitDeterministic(
        Split calldata _splitParams,
        address _owner,
        address _creator,
        bytes32 _salt
    ) external returns (address split);

    /**
     * @notice Predict the address of a new split and check if it is deployed.
     * @param _splitParams Params to create split with.
     * @param _owner Owner of created split.
     * @param _salt Salt for create2.
     */
    function isDeployed(
        Split calldata _splitParams,
        address _owner,
        bytes32 _salt
    ) external view returns (address split, bool exists);
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

/**
 * @title This interface adds support for ranked auction minting.
 * @author Art Blocks Inc.
 */
interface ISharedMinterRAMV0 {
    function createBid(
        uint256 projectId,
        address coreContract,
        uint16 slotIndex
    ) external payable;

    function topUpBid(
        uint256 projectId,
        address coreContract,
        uint32 bidId,
        uint16 newSlotIndex
    ) external payable;
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

/**
 * @title ISharedMinterRequired
 * @notice This interface contains the minimum required interface for a shared
 * minter contract. All custom, one-off minter contracts should implement this
 * interface.
 */
interface ISharedMinterRequired {
    /**
     * @notice Returns the minter's type, used by the minter filter for metadata
     * purposes.
     * @return The minter type.
     */
    function minterType() external view returns (string memory);

    /**
     * @notice Returns the minter's associated shared minter filter address.
     * @dev used by subgraph indexing service for entity relation purposes.
     * @return The minter filter address.
     */
    function minterFilterAddress() external returns (address);
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

import {ISharedMinterRequired} from "./ISharedMinterRequired.sol";

/**
 * @title ISharedMinterV0
 * @notice This interface extends the minimum required interface for a shared
 * minter contract to add additional functionality that is generally available
 * for all shared minter contracts on the shared minter filter.
 * @dev Custom, one-off minter contracts that are not globally approved may
 * choose to not implement this interface, but should still implement the
 * ISharedMinterRequired interface.
 */
interface ISharedMinterV0 is ISharedMinterRequired {
    // Sets the local max invocations for a given project, checking that the provided max invocations is
    // less than or equal to the global max invocations for the project set on the core contract.
    // This does not impact the max invocations value defined on the core contract.
    function manuallyLimitProjectMaxInvocations(
        uint256 projectId,
        address coreContract,
        uint24 maxInvocations
    ) external;

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

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

// SPDX-License-Identifier: LGPL-3.0-only
// Creatd By: Art Blocks Inc.

pragma solidity ^0.8.0;

import {ISplitFactoryV2} from "./integration-refs/splits-0x-v2/ISplitFactoryV2.sol";

interface ISplitProviderV0 {
    /**
     * @notice SplitInputs struct defines the inputs for requested splitters.
     * It is defined in a way easily communicated from the Art Blocks GenArt721V3 contract,
     * to allow for easy integration and minimal additional bytecode in the GenArt721V3 contract.
     */
    struct SplitInputs {
        address platformProviderSecondarySalesAddress;
        uint16 platformProviderSecondarySalesBPS;
        address renderProviderSecondarySalesAddress;
        uint16 renderProviderSecondarySalesBPS;
        uint8 artistTotalRoyaltyPercentage;
        address artist;
        address additionalPayee;
        uint8 additionalPayeePercentage;
    }

    /**
     * @notice Emitted when a new splitter contract is created.
     * @param splitter address of the splitter contract
     */
    event SplitterCreated(address indexed splitter);

    /**
     * @notice Gets or creates an immutable splitter contract at a deterministic address.
     * Splits in the splitter contract are determined by the input split parameters,
     * so we can safely create the splitter contract at a deterministic address (or use
     * the existing splitter contract if it already exists at that address).
     * @dev Uses the 0xSplits v2 implementation to create a splitter contract
     * @param splitInputs The split input parameters.
     * @return splitter The newly created splitter contract address.
     */
    function getOrCreateSplitter(
        SplitInputs calldata splitInputs
    ) external returns (address);

    /**
     * @notice Indicates the type of the contract, e.g. `SplitProviderV0`.
     * @return type_ The type of the contract.
     */
    function type_() external pure returns (bytes32);
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

/**
 * @title Art Blocks Helpers Library
 * @notice This library contains helper functions for common operations in the
 * Art Blocks ecosystem of smart contracts.
 * @author Art Blocks Inc.
 */

library ABHelpers {
    uint256 constant ONE_MILLION = 1_000_000;

    /**
     * @notice Function to convert token id to project id.
     * @param tokenId The id of the token.
     */
    function tokenIdToProjectId(
        uint256 tokenId
    ) internal pure returns (uint256) {
        // int division properly rounds down
        // @dev no way to disable division by zero check in solidity v0.8.24, so not unchecked
        return tokenId / ONE_MILLION;
    }

    /**
     * @notice Function to convert token id to token number.
     * @param tokenId The id of the token.
     */
    function tokenIdToTokenNumber(
        uint256 tokenId
    ) internal pure returns (uint256) {
        // mod returns remainder, which is the token number
        // @dev no way to disable mod zero check in solidity, so not unchecked
        return tokenId % ONE_MILLION;
    }

    /**
     * @notice Function to convert token id to token invocation.
     * @dev token invocation is the token number plus one, because token #0 is
     * invocation 1.
     * @param tokenId The id of the token.
     */
    function tokenIdToTokenInvocation(
        uint256 tokenId
    ) internal pure returns (uint256) {
        unchecked {
            // mod returns remainder, which is the token number
            // @dev no way to disable mod zero check in solidity, unchecked to optimize gas for addition
            return (tokenId % ONE_MILLION) + 1;
        }
    }

    /**
     * @notice Function to convert project id and token number to token id.
     * @param projectId The id of the project.
     * @param tokenNumber The token number.
     */
    function tokenIdFromProjectIdAndTokenNumber(
        uint256 projectId,
        uint256 tokenNumber
    ) internal pure returns (uint256) {
        // @dev intentionally not unchecked to ensure overflow detection, which
        // would likley only occur in a malicious call
        return (projectId * ONE_MILLION) + tokenNumber;
    }
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

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

/**
 * @title Art Blocks Authorization Minter Library
 * @notice This library contains helper functions that may be used contracts to
 * check authorization for performing operations in the Art Blocks V3 core
 * contract ecosystem.
 * @author Art Blocks Inc.
 */

library AuthLib {
    /**
     * @notice Function to restrict access to only AdminACL allowed calls, where
     * AdminACL is the admin of an IMinterFilterV1.
     * Reverts if not allowed.
     * @param minterFilterAddress address of the minter filter to be checked,
     * should implement IMinterFilterV1
     * @param sender address of the caller
     * @param contract_ address of the contract being called
     * @param selector selector of the function being called
     */
    function onlyMinterFilterAdminACL(
        address minterFilterAddress,
        address sender,
        address contract_,
        bytes4 selector
    ) internal {
        require(
            _minterFilterAdminACLAllowed({
                minterFilterAddress: minterFilterAddress,
                sender: sender,
                contract_: contract_,
                selector: selector
            }),
            "Only MinterFilter AdminACL"
        );
    }

    /**
     * @notice Function to restrict access to only AdminACL allowed calls, where
     * AdminACL is the admin of a core contract at `coreContract`.
     * Reverts if not allowed.
     * @param coreContract address of the core contract to be checked
     * @param sender address of the caller
     * @param contract_ address of the contract being called
     * @param selector selector of the function being called
     */
    function onlyCoreAdminACL(
        address coreContract,
        address sender,
        address contract_,
        bytes4 selector
    ) internal {
        require(
            _coreAdminACLAllowed({
                coreContract: coreContract,
                sender: sender,
                contract_: contract_,
                selector: selector
            }),
            "Only Core AdminACL allowed"
        );
    }

    /**
     * @notice Throws if `sender` is any account other than the artist of the
     * specified project `projectId` on core contract `coreContract`.
     * @param projectId The ID of the project being checked.
     * @param coreContract The address of the GenArt721CoreContractV3_Base
     * contract.
     * @param sender Wallet to check. Typically, the address of the caller.
     * @dev `sender` must be the artist associated with `projectId` on `coreContract`.
     */
    function onlyArtist(
        uint256 projectId,
        address coreContract,
        address sender
    ) internal view {
        require(
            _senderIsArtist({
                projectId: projectId,
                coreContract: coreContract,
                sender: sender
            }),
            "Only Artist"
        );
    }

    /**
     * @notice Function to restrict access to only the artist of a project, or AdminACL
     * allowed calls, where AdminACL is the admin of a core contract at
     * `coreContract`.
     * @param projectId id of the project
     * @param coreContract address of the core contract to be checked
     * @param sender address of the caller
     * @param contract_ address of the contract being called
     * @param selector selector of the function being called
     */
    function onlyCoreAdminACLOrArtist(
        uint256 projectId,
        address coreContract,
        address sender,
        address contract_,
        bytes4 selector
    ) internal {
        require(
            _senderIsArtist({
                projectId: projectId,
                coreContract: coreContract,
                sender: sender
            }) ||
                _coreAdminACLAllowed({
                    coreContract: coreContract,
                    sender: sender,
                    contract_: contract_,
                    selector: selector
                }),
            "Only Artist or Core Admin ACL"
        );
    }

    // ------------------------------------------------------------------------
    // Private functions used internally by this library
    // ------------------------------------------------------------------------

    /**
     * @notice Private function that returns if minter filter contract's AdminACL
     * allows `sender` to call function with selector `selector` on contract
     * `contract`.
     * @param minterFilterAddress address of the minter filter to be checked.
     * Should implement IMinterFilterV1.
     * @param sender address of the caller
     * @param contract_ address of the contract being called
     * @param selector selector of the function being called
     */
    function _minterFilterAdminACLAllowed(
        address minterFilterAddress,
        address sender,
        address contract_,
        bytes4 selector
    ) private returns (bool) {
        return
            IMinterFilterV1(minterFilterAddress).adminACLAllowed({
                sender: sender,
                contract_: contract_,
                selector: selector
            });
    }

    /**
     * @notice Private function that returns if core contract's AdminACL allows
     * `sender` to call function with selector `selector` on contract
     * `contract`.
     * @param coreContract address of the core contract to be checked
     * @param sender address of the caller
     * @param contract_ address of the contract being called
     * @param selector selector of the function being called
     */
    function _coreAdminACLAllowed(
        address coreContract,
        address sender,
        address contract_,
        bytes4 selector
    ) private returns (bool) {
        return
            IGenArt721CoreContractV3_Base(coreContract).adminACLAllowed({
                _sender: sender,
                _contract: contract_,
                _selector: selector
            });
    }

    /**
     * @notice Private function that returns if `sender` is the artist of `projectId`
     * on `coreContract`.
     * @param projectId project ID to check
     * @param coreContract core contract to check
     * @param sender wallet to check
     */
    function _senderIsArtist(
        uint256 projectId,
        address coreContract,
        address sender
    ) private view returns (bool senderIsArtist) {
        return
            sender ==
            IGenArt721CoreContractV3_Base(coreContract)
                .projectIdToArtistAddress(projectId);
    }
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

/**
 * @dev Library for using uint256 as a mapping to 256 bool values via a bit map.
 * This is useful for storing a large number of bool values in a compact way.
 * @dev This implementation is similar to OpenZeppelin's BitMaps library, but a
 * single uint256 is used directly in memory instead of operating within a
 * a mapping within a storage struct.
 * This design limits the number of indices to 256, but is more gas efficient
 * for use cases that fit within that limit. This is especially true for
 * operations that require many reads/writes, since SLOAD/SSTORE can be managed
 * outside of the library.
 */
library BitMaps256 {
    /**
     * @notice Checks if the bit at a specific index in the bit map is set.
     * A bit is considered set if it is 1, and unset if it is 0.
     * @param bitMap BitMap to check.
     * @param index The index of the bit to check.
     * @return Indicating if the bit at the specified index is set, false otherwise.
     */
    function get(uint256 bitMap, uint8 index) internal pure returns (bool) {
        uint256 mask = 1 << index;
        return bitMap & mask != 0;
    }

    /**
     * @notice Sets the bit at a specific index in the bit map to 1.
     * This function creates a new bit map where the bit at the specified index is set,
     * leaving other bits unchanged.
     * @param bitMap The original BitMap.
     * @param index The index of the bit to set.
     * @return newBitMap The new bit map after setting the bit at the specified index.
     */
    function set(
        uint256 bitMap,
        uint8 index
    ) internal pure returns (uint256 newBitMap) {
        uint256 mask = 1 << index;
        return bitMap | mask;
    }

    /**
     * @notice Unsets the bit at a specific index in the bit map, setting it to 0.
     * This function creates a new bit map where the bit at the specified index is unset,
     * leaving other bits unchanged.
     * @param bitMap The original BitMap.
     * @param index The index of the bit to unset.
     * @return newBitMap The new bit map after unsetting the bit at the specified index.
     */
    function unset(
        uint256 bitMap,
        uint8 index
    ) internal pure returns (uint256 newBitMap) {
        uint256 mask = 1 << index;
        return bitMap & ~mask;
    }

    /**
     * @notice Finds the index of the first bit that is set in the bit map
     * starting from a given index.
     * Returns (255, false) if no set bits were found.
     * @param bitMap BitMap to search
     * @param startIndex Index to start searching from, inclusive
     * @return minIndex Index of first set bit, or 255 if no bits were found
     * @return foundSetBit True if a set bit was found, false otherwise
     */
    function minBitSet(
        uint256 bitMap,
        uint8 startIndex
    ) internal pure returns (uint256 minIndex, bool foundSetBit) {
        // check if there's any set bit at or above startIndex
        if ((bitMap >> startIndex) == 0) {
            return (255, false);
        }
        minIndex = startIndex;
        // @dev this is a linear search, optimized to start only if there's a set bit at or above startIndex
        // worst case 255 iterations in memory
        while (minIndex < 255 && !get(bitMap, uint8(minIndex))) {
            minIndex++;
        }
        foundSetBit = get(bitMap, uint8(minIndex));
    }

    /**
     * @notice Finds the index of the highest bit that is set in the bit map
     * starting from a given index and counting down.
     * Returns (0, false) if no set bits were found.
     * @param bitMap BitMap to search
     * @param startIndex Index to start searching from, inclusive
     * @return maxIndex Index of last set bit, or 0 if no bits were found
     * @return foundSetBit True if a set bit was found, false otherwise
     */
    function maxBitSet(
        uint256 bitMap,
        uint8 startIndex
    ) internal pure returns (uint256 maxIndex, bool foundSetBit) {
        if ((bitMap << (255 - startIndex)) == 0) {
            return (0, false);
        }

        maxIndex = startIndex;
        // @dev this is a linear search, worst case 255 iterations in memory
        while (maxIndex > 0 && !get(bitMap, uint8(maxIndex))) {
            maxIndex--;
        }
        foundSetBit = get(bitMap, uint8(maxIndex));
    }
}

File 29 of 33 : GenericMinterEventsLib.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

/**
 * @title Art Blocks Generic Events Library
 * @notice This library is designed to define a set of generic events that all
 * shared minter libraries may utilize to populate indexed extra minter details
 * @dev Strings not supported. Recommend conversion of (short) strings to
 * bytes32 to remain gas-efficient.
 * @author Art Blocks Inc.
 */
library GenericMinterEventsLib {
    /**
     * @notice Generic project minter configuration event. Removed key `key`
     * for project `projectId`.
     * @param projectId Project ID key was removed for
     * @param coreContract Core contract address that projectId is on
     * @param key Key removed
     */
    event ConfigKeyRemoved(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key
    );
    /// BOOL
    /**
     * @notice Generic project minter configuration event. Value of key
     * `key` was set to `value` for project `projectId`.
     * @param projectId Project ID key was set for
     * @param coreContract Core contract address that projectId is on
     * @param key Key set
     * @param value Value key was set to
     */
    event ConfigValueSet(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key,
        bool value
    );
    /// UINT256
    /**
     * @notice Generic project minter configuration event. Value of key
     * `key` was set to `value` for project `projectId`.
     * @param projectId Project ID key was set for
     * @param coreContract Core contract address that projectId is on
     * @param key Key set
     * @param value Value key was set to
     */
    event ConfigValueSet(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key,
        uint256 value
    );
    /**
     * @notice Generic project minter configuration event. Added value `value`
     * to the set of uint256 at key `key` for project `projectId`.
     * @param projectId Project ID key was set for
     * @param coreContract Core contract address that projectId is on
     * @param key Key modified
     * @param value Value added to the key's set
     */
    event ConfigValueAddedToSet(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key,
        uint256 value
    );
    /**
     * @notice Generic project minter configuration event. Removed value
     * `value` to the set of uint256 at key `key` for project `projectId`.
     * @param projectId Project ID key was set for
     * @param coreContract Core contract address that projectId is on
     * @param key Key modified
     * @param value Value removed from the key's set
     */
    event ConfigValueRemovedFromSet(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key,
        uint256 value
    );
    /// ADDRESS
    /**
     * @notice Generic project minter configuration event. Value of key
     * `key` was set to `value` for project `projectId`.
     * @param projectId Project ID key was set for
     * @param coreContract Core contract address that projectId is on
     * @param key Key set
     * @param value Value key was set to
     */
    event ConfigValueSet(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key,
        address value
    );
    /**
     * @notice Generic project minter configuration event. Added value `value`
     * to the set of addresses at key `key` for project `projectId`.
     * @param projectId Project ID key was set for
     * @param coreContract Core contract address that projectId is on
     * @param key Key modified
     * @param value Value added to the key's set
     */
    event ConfigValueAddedToSet(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key,
        address value
    );
    /**
     * @notice Generic project minter configuration event. Removed value
     * `value` to the set of addresses at key `key` for project `projectId`.
     * @param projectId Project ID key was set for
     * @param coreContract Core contract address that projectId is on
     * @param key Key modified
     * @param value Value removed from the key's set
     */
    event ConfigValueRemovedFromSet(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key,
        address value
    );
    /// BYTES32
    /**
     * @notice Generic project minter configuration event. Value of key
     * `key` was set to `value` for project `projectId`.
     * @param projectId Project ID key was set for
     * @param coreContract Core contract address that projectId is on
     * @param key Key set
     * @param value Value key was set to
     */
    event ConfigValueSet(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key,
        bytes32 value
    );
    /**
     * @notice Generic project minter configuration event. Added value `value`
     * to the set of bytes32 at key `key` for project `projectId`.
     * @param projectId Project ID key was set for
     * @param coreContract Core contract address that projectId is on
     * @param key Key modified
     * @param value Value added to the key's set
     */
    event ConfigValueAddedToSet(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key,
        bytes32 value
    );
    /**
     * @notice Generic project minter configuration event. Removed value
     * `value` to the set of bytes32 at key `key` for project `projectId`.
     * @param projectId Project ID key was set for
     * @param coreContract Core contract address that projectId is on
     * @param key Key modified
     * @param value Value removed from the key's set
     */
    event ConfigValueRemovedFromSet(
        uint256 indexed projectId,
        address indexed coreContract,
        bytes32 key,
        bytes32 value
    );
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

import {IGenArt721CoreContractV3_Base} from "../../../interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol";

import {ABHelpers} from "../ABHelpers.sol";

import {Math} from "@openzeppelin-4.7/contracts/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin-4.7/contracts/utils/math/SafeCast.sol";

/**
 * @title Art Blocks Max Invocations Library
 * @notice This library manages the maximum invocation limits for Art Blocks
 * projects. It provides functionality for synchronizing, manually limiting, and
 * updating these limits, ensuring the integrity in relation to the core Art
 * Blocks contract, and managing updates upon token minting.
 * @dev Functions include `syncProjectMaxInvocationsToCore`,
 * `manuallyLimitProjectMaxInvocations`, and `purchaseEffectsInvocations`.
 * @author Art Blocks Inc.
 */

library MaxInvocationsLib {
    using SafeCast for uint256;

    /**
     * @notice Local max invocations for project `projectId`, tied to core contract `coreContractAddress`,
     * updated to `maxInvocations`.
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     * @param maxInvocations The new max invocations limit.
     */
    event ProjectMaxInvocationsLimitUpdated(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 maxInvocations
    );

    // position of Max Invocations Lib storage, using a diamond storage pattern
    // for this library
    bytes32 constant MAX_INVOCATIONS_LIB_STORAGE_POSITION =
        keccak256("maxinvocationslib.storage");

    uint256 internal constant ONE_MILLION = 1_000_000;

    /**
     * @notice Data structure that holds max invocations project configuration.
     */
    struct MaxInvocationsProjectConfig {
        bool maxHasBeenInvoked;
        uint24 maxInvocations;
    }

    // Diamond storage pattern is used in this library
    struct MaxInvocationsLibStorage {
        mapping(address coreContract => mapping(uint256 projectId => MaxInvocationsProjectConfig)) maxInvocationsProjectConfigs;
    }

    /**
     * @notice Syncs project's max invocations to core contract value.
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     */
    function syncProjectMaxInvocationsToCore(
        uint256 projectId,
        address coreContract
    ) internal {
        (
            uint256 coreInvocations,
            uint256 coreMaxInvocations
        ) = coreContractInvocationData({
                projectId: projectId,
                coreContract: coreContract
            });
        // update storage with results
        MaxInvocationsProjectConfig
            storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        // @dev only bugged core would return > 1e6 invocations, but safe-cast
        // for additional overflow safety
        maxInvocationsProjectConfig.maxInvocations = coreMaxInvocations
            .toUint24();

        // We need to ensure maxHasBeenInvoked is correctly set after manually syncing the
        // local maxInvocations value with the core contract's maxInvocations value.
        maxInvocationsProjectConfig.maxHasBeenInvoked =
            coreInvocations == coreMaxInvocations;

        emit ProjectMaxInvocationsLimitUpdated({
            projectId: projectId,
            coreContract: coreContract,
            maxInvocations: coreMaxInvocations
        });
    }

    /**
     * @notice Manually limits project's max invocations.
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     * @param maxInvocations The new max invocations limit.
     */
    function manuallyLimitProjectMaxInvocations(
        uint256 projectId,
        address coreContract,
        uint24 maxInvocations
    ) internal {
        // CHECKS
        (
            uint256 coreInvocations,
            uint256 coreMaxInvocations
        ) = coreContractInvocationData({
                projectId: projectId,
                coreContract: coreContract
            });
        require(
            maxInvocations <= coreMaxInvocations,
            "Invalid max invocations"
        );
        require(maxInvocations >= coreInvocations, "Invalid max invocations");

        // EFFECTS
        MaxInvocationsProjectConfig
            storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        // update storage with results
        maxInvocationsProjectConfig.maxInvocations = uint24(maxInvocations);
        // We need to ensure maxHasBeenInvoked is correctly set after manually setting the
        // local maxInvocations value.
        maxInvocationsProjectConfig.maxHasBeenInvoked =
            coreInvocations == maxInvocations;

        emit ProjectMaxInvocationsLimitUpdated({
            projectId: projectId,
            coreContract: coreContract,
            maxInvocations: maxInvocations
        });
    }

    /**
     * @notice Validate effects on invocations after purchase. This ensures
     * that the token invocation is less than or equal to the local max
     * invocations, and also updates the local maxHasBeenInvoked value.
     * @dev This function checks that the token invocation is less than or
     * equal to the local max invocations, and also updates the local
     * maxHasBeenInvoked value.
     * @param tokenId The id of the token.
     * @param coreContract The address of the core contract.
     */
    function validateMintEffectsInvocations(
        uint256 tokenId,
        address coreContract
    ) internal {
        uint256 projectId = ABHelpers.tokenIdToProjectId(tokenId);
        MaxInvocationsProjectConfig
            storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        // invocation is token number plus one, and will never overflow due to
        // limit of 1e6 invocations per project.
        uint256 tokenInvocation = ABHelpers.tokenIdToTokenInvocation(tokenId);
        uint256 localMaxInvocations = maxInvocationsProjectConfig
            .maxInvocations;
        // handle the case where the token invocation == minter local max
        // invocations occurred on a different minter, and we have a stale
        // local maxHasBeenInvoked value returning a false negative.
        // @dev this is a CHECK after EFFECTS, so security was considered
        // in detail here.
        require(
            tokenInvocation <= localMaxInvocations,
            "Max invocations reached"
        );
        // in typical case, update the local maxHasBeenInvoked value
        // to true if the token invocation == minter local max invocations
        // (enables gas efficient reverts after sellout)
        if (tokenInvocation == localMaxInvocations) {
            maxInvocationsProjectConfig.maxHasBeenInvoked = true;
        }
    }

    /**
     * @notice Checks that the max invocations have not been reached for a
     * given project. This only checks the minter's local max invocations, and
     * does not consider the core contract's max invocations.
     * The function reverts if the max invocations have been reached.
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     */
    function preMintChecks(
        uint256 projectId,
        address coreContract
    ) internal view {
        // check that max invocations have not been reached
        MaxInvocationsProjectConfig
            storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        require(
            !maxInvocationsProjectConfig.maxHasBeenInvoked,
            "Max invocations reached"
        );
    }

    /**
     * @notice Helper function to check if max invocations has not been initialized.
     * Returns true if not initialized, false if initialized.
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     * @dev We know a project's max invocations have never been initialized if
     * both max invocations and maxHasBeenInvoked are still initial values.
     * This is because if maxInvocations were ever set to zero,
     * maxHasBeenInvoked would be set to true.
     */
    function maxInvocationsIsUnconfigured(
        uint256 projectId,
        address coreContract
    ) internal view returns (bool) {
        MaxInvocationsProjectConfig
            storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        return
            maxInvocationsProjectConfig.maxInvocations == 0 &&
            !maxInvocationsProjectConfig.maxHasBeenInvoked;
    }

    /**
     * @notice Function returns if invocations remain available for a given project.
     * This function calls the core contract to get the most up-to-date
     * invocation data (which may be useful to avoid reverts during mint).
     * This function considers core contract max invocations, and minter local
     * max invocations, and returns a response based on the most limiting
     * max invocations value.
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     */
    function invocationsRemainOnCore(
        uint256 projectId,
        address coreContract
    ) internal view returns (bool) {
        return
            getInvocationsAvailable({
                projectId: projectId,
                coreContract: coreContract
            }) != 0;
    }

    /**
     * @notice Pulls core contract invocation data for a given project.
     * @dev This function calls the core contract to get the invocation data
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     * @return coreInvocations The number of invocations for the project.
     * @return coreMaxInvocations The max invocations for the project, as
     * defined on the core contract.
     */
    function coreContractInvocationData(
        uint256 projectId,
        address coreContract
    )
        internal
        view
        returns (uint256 coreInvocations, uint256 coreMaxInvocations)
    {
        (
            coreInvocations,
            coreMaxInvocations,
            ,
            ,
            ,

        ) = IGenArt721CoreContractV3_Base(coreContract).projectStateData(
            projectId
        );
    }

    /**
     * @notice Function returns the max invocations for a given project.
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     * to be queried.
     */
    function getMaxInvocations(
        uint256 projectId,
        address coreContract
    ) internal view returns (uint256) {
        MaxInvocationsProjectConfig
            storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        return maxInvocationsProjectConfig.maxInvocations;
    }

    /**
     * @notice Function returns if max has been invoked for a given project.
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     * to be queried.
     */
    function getMaxHasBeenInvoked(
        uint256 projectId,
        address coreContract
    ) internal view returns (bool) {
        MaxInvocationsProjectConfig
            storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        return maxInvocationsProjectConfig.maxHasBeenInvoked;
    }

    /**
     * @notice Function returns if a project has reached its max invocations.
     * Function is labelled as "safe" because it checks the core contract's
     * invocations and max invocations. If the local max invocations is greater
     * than the core contract's max invocations, it will defer to the core
     * contract's max invocations (since those are the limiting factor).
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     */
    function projectMaxHasBeenInvokedSafe(
        uint256 projectId,
        address coreContract
    ) internal view returns (bool) {
        return
            getInvocationsAvailable({
                projectId: projectId,
                coreContract: coreContract
            }) == 0;
    }

    /**
     * @notice Function returns the number of invocations available for a given
     * project. Function checks the core contract's invocations and minter's max
     * invocations, ensuring that the most limiting value is used, even if the
     * local minter max invocations is stale.
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     * @return Number of invocations available for the project.
     */
    function getInvocationsAvailable(
        uint256 projectId,
        address coreContract
    ) internal view returns (uint256) {
        // get max invocations from core contract
        (
            uint256 coreInvocations,
            uint256 coreMaxInvocations
        ) = coreContractInvocationData({
                projectId: projectId,
                coreContract: coreContract
            });
        MaxInvocationsProjectConfig
            storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        uint256 limitingMaxInvocations = Math.min(
            coreMaxInvocations,
            maxInvocationsProjectConfig.maxInvocations // local max invocations
        );
        // if core invocations are greater than the limiting max invocations,
        // return 0 since no invocations remain
        if (coreInvocations >= limitingMaxInvocations) {
            return 0;
        }
        // otherwise, return the number of invocations remaining
        // @dev will not undeflow due to previous check
        return limitingMaxInvocations - coreInvocations;
    }

    /**
     * @notice Refreshes max invocations to account for core contract max
     * invocations state, without imposing any additional restrictions on the
     * minter's max invocations state.
     * If minter max invocations have never been populated, this function will
     * populate them to equal the core contract's max invocations state (which
     * is the least restrictive state).
     * If minter max invocations have been populated, this function will ensure
     * the minter's max invocations are not greater than the core contract's
     * max invocations (which would be stale and illogical), and update the
     * minter's max invocations and maxHasBeenInvoked state to be consistent
     * with the core contract's max invocations.
     * If the minter max invocations have been populated and are not greater
     * than the core contract's max invocations, this function will do nothing,
     * since that is a valid state in which the minter has been configured to
     * be more restrictive than the core contract.
     * @dev assumes core contract's max invocations may only be reduced, which
     * is the case for all V3 core contracts
     * @param projectId The id of the project.
     * @param coreContract The address of the core contract.
     */
    function refreshMaxInvocations(
        uint256 projectId,
        address coreContract
    ) internal {
        MaxInvocationsProjectConfig
            storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        if (maxInvocationsIsUnconfigured(projectId, coreContract)) {
            // populate the minter max invocation state to equal the values on
            // the core contract (least restrictive state)
            syncProjectMaxInvocationsToCore({
                projectId: projectId,
                coreContract: coreContract
            });
        } else {
            // if local max invocations were already populated, validate the local state
            (
                uint256 coreInvocations,
                uint256 coreMaxInvocations
            ) = coreContractInvocationData({
                    projectId: projectId,
                    coreContract: coreContract
                });

            uint256 localMaxInvocations = maxInvocationsProjectConfig
                .maxInvocations;
            if (localMaxInvocations > coreMaxInvocations) {
                // if local max invocations are greater than core max invocations, make
                // them equal since that is the least restrictive logical state
                // @dev this is only possible if the core contract's max invocations
                // have been reduced since the minter's max invocations were last
                // updated
                // set local max invocations to core contract's max invocations
                maxInvocationsProjectConfig.maxInvocations = uint24(
                    coreMaxInvocations
                );
                // update the minter's `maxHasBeenInvoked` state
                maxInvocationsProjectConfig
                    .maxHasBeenInvoked = (coreMaxInvocations ==
                    coreInvocations);
                emit ProjectMaxInvocationsLimitUpdated({
                    projectId: projectId,
                    coreContract: coreContract,
                    maxInvocations: coreMaxInvocations
                });
            } else if (coreInvocations >= localMaxInvocations) {
                // core invocations are greater than this minter's max
                // invocations, indicating that minting must have occurred on
                // another minter. update the minter's `maxHasBeenInvoked` to
                // true to prevent any false negatives on
                // `getMaxHasBeenInvoked'
                maxInvocationsProjectConfig.maxHasBeenInvoked = true;
                // @dev do not emit event, because we did not change the value
                // of minter-local max invocations
            }
        }
    }

    /**
     * @notice Loads the MaxInvocationsProjectConfig for a given project and core
     * contract.
     * @param projectId Project Id to get config for
     * @param coreContract Core contract address to get config for
     */
    function getMaxInvocationsProjectConfig(
        uint256 projectId,
        address coreContract
    ) internal view returns (MaxInvocationsProjectConfig storage) {
        return s().maxInvocationsProjectConfigs[coreContract][projectId];
    }

    /**
     * @notice Return the storage struct for reading and writing. This library
     * uses a diamond storage pattern when managing storage.
     * @return storageStruct The MaxInvocationsLibStorage struct.
     */
    function s()
        internal
        pure
        returns (MaxInvocationsLibStorage storage storageStruct)
    {
        bytes32 position = MAX_INVOCATIONS_LIB_STORAGE_POSITION;
        assembly ("memory-safe") {
            storageStruct.slot := position
        }
    }
}

File 31 of 33 : RAMLib.sol
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

import {IMinterFilterV1} from "../../../interfaces/v0.8.x/IMinterFilterV1.sol";

import {BitMaps256} from "../BitMap.sol";
import {PackedBools} from "../PackedBools.sol";
import {ABHelpers} from "../ABHelpers.sol";
import {SplitFundsLib} from "./SplitFundsLib.sol";
import {MaxInvocationsLib} from "./MaxInvocationsLib.sol";
import {GenericMinterEventsLib} from "./GenericMinterEventsLib.sol";

import {IERC721} from "@openzeppelin-5.0/contracts/token/ERC721/IERC721.sol";
import {SafeCast} from "@openzeppelin-5.0/contracts/utils/math/SafeCast.sol";
import {Math} from "@openzeppelin-5.0/contracts/utils/math/Math.sol";

/**
 * @title Art Blocks Ranked Auction Minter (RAM) Library
 * @notice This library is designed for the Art Blocks platform. It includes
 * Structs and functions that help with ranked auction minters.
 * @author Art Blocks Inc.
 */
library RAMLib {
    using SafeCast for uint256;
    using BitMaps256 for uint256;
    using PackedBools for uint256;
    /**
     * @notice Minimum auction length, in seconds, was updated to be the
     * provided value.
     * @param minAuctionDurationSeconds Minimum auction length, in seconds
     */
    event MinAuctionDurationSecondsUpdated(uint256 minAuctionDurationSeconds);

    /**
     * @notice Admin-controlled refund gas limit updated
     * @param refundGasLimit Gas limit to use when refunding the previous
     * highest bidder, prior to using fallback force-send to refund
     */
    event MinterRefundGasLimitUpdated(uint24 refundGasLimit);

    /**
     * @notice Number of slots used by this RAM minter
     * @param numSlots Number of slots used by this RAM minter
     */
    event NumSlotsUpdated(uint256 numSlots);

    /**
     * @notice RAM auction buffer time parameters updated
     * @param auctionBufferSeconds time period at end of auction when new bids
     * can affect auction end time, updated to be this many seconds after the
     * bid is placed.
     * @param maxAuctionExtraSeconds maximum amount of time that can be added
     * to the auction end time due to new bids.
     */
    event AuctionBufferTimeParamsUpdated(
        uint256 auctionBufferSeconds,
        uint256 maxAuctionExtraSeconds
    );

    /**
     * @notice Admin minting constraint configuration updated
     * @param coreContract Core contract address to update
     * @param adminMintingConstraint enum representing admin minting constraint imposed on this contract
     */
    event ContractConfigUpdated(
        address indexed coreContract,
        AdminMintingConstraint adminMintingConstraint
    );

    /**
     * @notice Auction parameters updated
     * @param projectId Project Id to update
     * @param coreContract Core contract address to update
     * @param timestampStart Auction start timestamp
     * @param timestampEnd Auction end timestamp
     * @param basePrice Auction base price
     * @param allowExtraTime Auction allows extra time
     * @param adminArtistOnlyMintPeriodIfSellout Auction admin-artist-only mint period if
     * sellout
     * @param numTokensInAuction Number of tokens in auction
     */
    event AuctionConfigUpdated(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 timestampStart,
        uint256 timestampEnd,
        uint256 basePrice,
        bool allowExtraTime,
        bool adminArtistOnlyMintPeriodIfSellout,
        uint256 numTokensInAuction
    );

    /**
     * @notice Number of tokens in auction updated
     * @dev okay to not index this event if prior to AuctionConfigUpdated, as
     * the state value will be emitted in another future event
     * @dev generic event not used due to additional indexing logic desired
     * @param projectId Project Id to update
     * @param coreContract Core contract address to update
     * @param numTokensInAuction Number of tokens in auction
     */
    event NumTokensInAuctionUpdated(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 numTokensInAuction
    );

    /**
     * @notice Auction timestamp end updated. Occurs when auction is extended
     * due to new bids near the end of an auction, when the auction is
     * configured to allow extra time.
     * Also may occur when an admin extends the auction within the emergency
     * extension time limit.
     * @dev generic event not used due to additional indexing logic desired
     * when event is encountered (want to understand what caused time
     * extension)
     * @param projectId Project Id to update
     * @param coreContract Core contract address to update
     * @param timestampEnd Auction end timestamp
     */
    event AuctionTimestampEndUpdated(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 timestampEnd
    );

    /**
     * @notice Bid created in auction
     * @param projectId Project Id to update
     * @param coreContract Core contract address to update
     * @param slotIndex Slot index of bid that was created
     * @param bidId Bid Id that was created
     * @param bidder Address of bidder
     */
    event BidCreated(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 slotIndex,
        uint256 bidId,
        address bidder
    );

    /**
     * @notice Bid removed from auction because it was outbid.
     * @param projectId Project Id to update
     * @param coreContract Core contract address to update
     * @param bidId Bid Id that was removed
     */
    event BidRemoved(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 bidId
    );

    /**
     * @notice Bid topped up in auction
     * @param projectId Project Id to update
     * @param coreContract Core contract address to update
     * @param bidId Bid Id that was topped up
     * @param newSlotIndex New slot index of bid that was topped up
     */
    event BidToppedUp(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 bidId,
        uint256 newSlotIndex
    );

    /**
     * @notice Bid was settled, and any payment above the lowest winning bid,
     * or base price if not a sellout, was refunded to the bidder.
     * @param projectId Project Id to update
     * @param coreContract Core contract address to update
     * @param bidId ID of bid that was settled
     */
    event BidSettled(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 bidId
    );

    /**
     * @notice A token was minted to the bidder for bid `bidId`. The tokenId is
     * the token that was minted.
     * @param projectId Project Id to update
     * @param coreContract Core contract address to update
     * @param bidId ID of bid that was settled
     * @param tokenId Token Id that was minted
     *
     */
    event BidMinted(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 bidId,
        uint256 tokenId
    );

    /**
     * @notice Bid was refunded, and the entire bid value was sent to the
     * bidder.
     * This only occurs if the minter encountered an unexpected error state
     * due to operational issues, and the minter was unable to mint a token to
     * the bidder.
     * @param projectId Project Id to update
     * @param coreContract Core contract address to update
     * @param bidId ID of bid that was settled
     */
    event BidRefunded(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 bidId
    );

    /**
     * @notice Token was directly purchased after an auction ended, and the
     * token was minted to the buyer.
     * @param projectId Project Id to update
     * @param coreContract Core contract address to update
     * @param tokenId Token Id that was minted
     * @param to Address that the token was minted to
     */
    event TokenPurchased(
        uint256 indexed projectId,
        address indexed coreContract,
        uint256 tokenId,
        address to
    );

    // position of RAM Lib storage, using a diamond storage pattern
    // for this library
    bytes32 constant RAM_LIB_STORAGE_POSITION = keccak256("ramlib.storage");

    // generic event key constants
    bytes32 internal constant CONFIG_AUCTION_REVENUES_COLLECTED =
        "auctionRevenuesCollected";
    bytes32 internal constant CONFIG_TIMESTAMP_END = "timestampEnd";

    uint256 constant NUM_SLOTS = 512;

    // pricing assumes maxPrice = minPrice * 2^8, pseudo-exponential curve
    uint256 constant SLOTS_PER_PRICE_DOUBLE = NUM_SLOTS / 8; // 64 slots per double

    // auction extension time constants
    uint256 constant AUCTION_BUFFER_SECONDS = 5 minutes;
    uint256 constant MAX_AUCTION_EXTRA_SECONDS = 1 hours;
    // @dev store value in hours to improve storage packing
    uint256 constant MAX_AUCTION_ADMIN_EMERGENCY_EXTENSION_HOURS = 72; // 72 hours

    uint256 constant ADMIN_ARTIST_ONLY_MINT_TIME_SECONDS = 72 hours;

    // packed bools constants for Bid struct
    uint8 constant INDEX_IS_SETTLED = 0;
    uint8 constant INDEX_IS_MINTED = 1;
    uint8 constant INDEX_IS_REFUNDED = 2;

    enum ProjectMinterStates {
        PreAuction, // Pre-Auction, State A
        LiveAuction, // Live-Auction, State B
        PostAuctionSellOutAdminArtistMint, // Post-Auction, sell out, not all bids handled, admin-artist-only mint period, State C
        PostAuctionOpenMint, // Post-Auction, not all bids handled, post-admin-artist-only mint period, State D
        PostAuctionAllBidsHandled // Post-Auction, all bids handled, State E
    }

    // project-specific parameters
    struct RAMProjectConfig {
        // mapping of all bids by Bid ID
        mapping(uint256 bidId => Bid) bids;
        // doubly linked list of bids for each slot
        mapping(uint256 slot => uint256 headBidId) headBidIdBySlot;
        mapping(uint256 slot => uint256 tailBidId) tailBidIdBySlot;
        // --- slot metadata for efficiency ---
        // two bitmaps with index set only if one or more active bids exist for
        // the corresponding slot. The first bitmap (A) is for slots 0-255, the
        // second bitmap (B) is for slots 256-511.
        uint256 slotsBitmapA;
        uint256 slotsBitmapB;
        // minimum bitmap index with an active bid
        // @dev set to 512 if no active bids
        // @dev max uint16 >> max possible value of 512
        uint16 minBidSlotIndex;
        // maximum bitmap index with an active bid
        // @dev set to 0 if no active bids
        uint16 maxBidSlotIndex;
        // --- bid auto-minting tracking ---
        uint32 latestMintedBidId;
        // --- error state bid auto-refund tracking ---
        uint32 latestRefundedBidId;
        // --- next bid ID ---
        // nonce for generating new bid IDs on this project
        // @dev allows for gt 4 billion bids, and max possible bids for a
        // 1M token project is 1M * 512 slots = 512M bids < 4B max uint32
        uint32 nextBidId;
        // --- auction parameters ---
        // number of tokens and related values
        // @dev max uint24 is 16,777,215 > 1_000_000 max project size
        uint24 numTokensInAuction;
        uint24 numBids;
        uint24 numBidsMintedTokens;
        uint24 numBidsErrorRefunded;
        // timing
        // @dev max uint40 ~= 1.1e12 sec ~= 34 thousand years
        uint40 timestampStart;
        // @dev timestampOriginalEnd & timestampEnd are the same if not in extra time
        uint40 timestampOriginalEnd;
        uint40 timestampEnd;
        // @dev max uint8 ~= 256 hours, which is gt max auction extension time of 72 hours
        uint8 adminEmergencyExtensionHoursApplied;
        bool allowExtraTime;
        bool adminArtistOnlyMintPeriodIfSellout;
        // pricing
        // @dev max uint88 ~= 3e26 Wei = ~300 million ETH, which is well above
        // the expected prices of any NFT mint in the foreseeable future.
        uint88 basePrice;
        // -- redundant backstops --
        // Track per-project fund balance, in wei. This is used as a redundant
        // backstop to prevent one project from draining the minter's balance
        // of ETH from other projects, which is a worthwhile failsafe on this
        // shared minter.
        // @dev max uint120 > max basPrice * 256 * 1_000_000
        // @dev while uint88 is gt max ETH supply, use uin120 to prevent reliance on
        // max token supply
        uint120 projectBalance;
        // --- revenue collection state ---
        bool revenuesCollected;
    }

    struct Bid {
        uint32 prevBidId;
        uint32 nextBidId;
        uint16 slotIndex;
        address bidder;
        // three bool values packed into a single uint8
        // index 0 - isSettled (INDEX_IS_SETTLED)
        // index 1 - isMinted (INDEX_IS_MINTED)
        // index 2 - isRefunded (INDEX_IS_REFUNDED)
        uint8 packedBools;
    }

    // contract-specific parameters
    enum AdminMintingConstraint {
        None,
        AdminArtistOnlyMintPeriod,
        NoAdminArtistOnlyMintPeriod
    }

    // Diamond storage pattern is used in this library
    struct RAMLibStorage {
        mapping(address coreContract => mapping(uint256 projectId => RAMProjectConfig)) RAMProjectConfigs;
        mapping(address => AdminMintingConstraint) RAMAdminMintingConstraint;
    }

    /**
     * @notice Update a contract's requirements on if a post-auction
     * admin-artist-only mint period is required or not, for and-on
     * configured projects.
     * @param coreContract The address of the core contract being configured
     * @param adminMintingConstraint The AdminMintingConstraint setting for the contract
     */
    function setContractConfig(
        address coreContract,
        AdminMintingConstraint adminMintingConstraint
    ) internal {
        // set the contract admin minting constraint with the new constraint enum value
        s().RAMAdminMintingConstraint[coreContract] = adminMintingConstraint;
        // emit event
        emit ContractConfigUpdated({
            coreContract: coreContract,
            adminMintingConstraint: adminMintingConstraint
        });
    }

    /**
     * @notice Function to add emergency auction hours to auction of
     * project `projectId` on core contract `coreContract`.
     * Protects against unexpected frontend downtime, etc.
     * Reverts if called by anyone other than a contract admin.
     * Reverts if project is not in a Live Auction.
     * Reverts if auction is already in extra time.
     * Reverts if adding more than the maximum number of emergency hours.
     * @param projectId Project ID to add emergency auction hours to.
     * @param coreContract Core contract address for the given project.
     * @param emergencyHoursToAdd Number of emergency hours to add to the
     * project's auction.
     */
    function adminAddEmergencyAuctionHours(
        uint256 projectId,
        address coreContract,
        uint8 emergencyHoursToAdd
    ) internal {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // CHECKS
        // require auction in state B (Live Auction)
        require(
            getProjectMinterState(projectId, coreContract) ==
                ProjectMinterStates.LiveAuction,
            "Only live auction"
        );
        // require auction has not reached extra time
        require(
            RAMProjectConfig_.timestampOriginalEnd ==
                RAMProjectConfig_.timestampEnd,
            "Not allowed in extra time"
        );
        // require auction is not being extended beyond limit
        uint8 newAdminEmergencyExtensionHours = RAMProjectConfig_
            .adminEmergencyExtensionHoursApplied + emergencyHoursToAdd;

        require(
            newAdminEmergencyExtensionHours <=
                MAX_AUCTION_ADMIN_EMERGENCY_EXTENSION_HOURS,
            "Only emergency hours lt max"
        );
        // calculate auction end time
        // @dev overflow automatically checked in solidity 0.8
        uint40 newTimestampEnd = RAMProjectConfig_.timestampEnd +
            emergencyHoursToAdd *
            1 hours;

        // EFFECTS
        // update emergency hours applied
        // @dev overflow automatically checked in solidity 0.8
        RAMProjectConfig_
            .adminEmergencyExtensionHoursApplied = newAdminEmergencyExtensionHours;

        // update auction end time
        // @dev update both original end timestamp and current end timestamp
        // because this is not extra time, but rather an emergency extension
        RAMProjectConfig_.timestampEnd = newTimestampEnd;
        RAMProjectConfig_.timestampOriginalEnd = newTimestampEnd;

        // emit event
        emit AuctionTimestampEndUpdated({
            projectId: projectId,
            coreContract: coreContract,
            timestampEnd: newTimestampEnd
        });
    }

    /**
     * @notice Function to set auction details on project `projectId` on core contract `coreContract`.
     * Reverts if not currently in ProjectMinterState A.
     * Reverts if base price does not meet the minimum.
     * Reverts if not for future auction.
     * Reverts if end time not greater than start time.
     * Reverts if adminArtistOnlyMintPeriodIfSellout disagrees with the admin configured constraint.
     * @param projectId Project ID to add emergency auction hours to.
     * @param coreContract Core contract address for the given project.
     * @param auctionTimestampStart New timestamp at which to start the auction.
     * @param auctionTimestampEnd New timestamp at which to end the auction.
     * @param basePrice Base price (or reserve price) of the auction, in Wei
     * @param allowExtraTime Auction allows extra time
     * @param adminArtistOnlyMintPeriodIfSellout Auction admin-artist-only mint period if
     * sellout
     */
    function setAuctionDetails(
        uint256 projectId,
        address coreContract,
        uint40 auctionTimestampStart,
        uint40 auctionTimestampEnd,
        uint88 basePrice,
        bool allowExtraTime,
        bool adminArtistOnlyMintPeriodIfSellout
    ) internal {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // CHECKS
        // require ProjectMinterState Pre-auction (State A)
        require(
            getProjectMinterState({
                projectId: projectId,
                coreContract: coreContract
            }) == ProjectMinterStates.PreAuction,
            "Only pre-auction"
        );
        // require base price >= 0.05 ETH
        require(basePrice >= 0.05 ether, "Only base price gte 0.05 ETH");
        // only future start time
        require(
            auctionTimestampStart > block.timestamp,
            "Only future auctions"
        );

        // enforce contract-level constraints set by contract admin
        AdminMintingConstraint RAMAdminMintingConstraint_ = getRAMAdminMintingConstraintValue({
                coreContract: coreContract
            });

        if (
            RAMAdminMintingConstraint_ ==
            AdminMintingConstraint.AdminArtistOnlyMintPeriod
        ) {
            require(
                adminArtistOnlyMintPeriodIfSellout,
                "Only admin-artist mint period"
            );
        } else if (
            RAMAdminMintingConstraint_ ==
            AdminMintingConstraint.NoAdminArtistOnlyMintPeriod
        ) {
            require(
                !adminArtistOnlyMintPeriodIfSellout,
                "Only no admin-artist mint period"
            );
        }

        // refresh max invocations to eliminate any stale state
        MaxInvocationsLib.refreshMaxInvocations({
            projectId: projectId,
            coreContract: coreContract
        });

        // set auction details
        RAMProjectConfig_.timestampStart = auctionTimestampStart;
        RAMProjectConfig_.timestampEnd = auctionTimestampEnd;
        RAMProjectConfig_.timestampOriginalEnd = auctionTimestampEnd;
        RAMProjectConfig_.basePrice = basePrice;
        RAMProjectConfig_.allowExtraTime = allowExtraTime;
        RAMProjectConfig_
            .adminArtistOnlyMintPeriodIfSellout = adminArtistOnlyMintPeriodIfSellout;
        // refresh numTokensInAuction
        uint256 numTokensInAuction = refreshNumTokensInAuction({
            projectId: projectId,
            coreContract: coreContract
        });

        // initialize min slot metadata to NUM_SLOTS (an invalid index) to represent NULL value
        RAMProjectConfig_.minBidSlotIndex = uint16(NUM_SLOTS);

        // emit state change event
        emit AuctionConfigUpdated({
            projectId: projectId,
            coreContract: coreContract,
            timestampStart: auctionTimestampStart,
            timestampEnd: auctionTimestampEnd,
            basePrice: basePrice,
            allowExtraTime: allowExtraTime,
            adminArtistOnlyMintPeriodIfSellout: adminArtistOnlyMintPeriodIfSellout,
            numTokensInAuction: numTokensInAuction
        });
    }

    /**
     * @notice Reduces the auction length for project `projectId` on core
     * contract `coreContract` to `auctionTimestampEnd`.
     * Only allowed to be called during a live auction, and protects against
     * the case of an accidental excessively long auction, which locks funds.
     * Reverts if called by anyone other than the project's artist.
     * Reverts if project is not in a Live Auction.
     * Reverts if auction is not being reduced in length.
     * Reverts if in extra time.
     * Reverts if `auctionTimestampEnd` results in auction that is not at least
     * `minimumAuctionDurationSeconds` in duration.
     * Reverts if admin previously applied a time extension.
     * @param projectId Project ID to reduce the auction length for.
     * @param coreContract Core contract address for the given project.
     * @param auctionTimestampEnd New timestamp at which to end the auction.
     * @param minimumAuctionDurationSeconds Minimum auction duration, in seconds
     */
    function reduceAuctionLength(
        uint256 projectId,
        address coreContract,
        uint40 auctionTimestampEnd,
        uint256 minimumAuctionDurationSeconds
    ) internal {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // CHECKS
        // require auction state B, live auction
        require(
            getProjectMinterState(projectId, coreContract) ==
                ProjectMinterStates.LiveAuction,
            "Only live auction"
        );
        // require no previous admin extension time
        require(
            RAMProjectConfig_.adminEmergencyExtensionHoursApplied == 0,
            "No previous admin extension"
        );
        // require not in extra time
        require(
            RAMProjectConfig_.timestampOriginalEnd ==
                RAMProjectConfig_.timestampEnd,
            "Not allowed in extra time"
        );
        // require reduction in auction length
        require(
            auctionTimestampEnd < RAMProjectConfig_.timestampEnd,
            "Only reduce auction length"
        );
        // require meet minimum auction length requirement
        require(
            auctionTimestampEnd >
                RAMProjectConfig_.timestampStart +
                    minimumAuctionDurationSeconds,
            "Auction too short"
        );
        // require new end time in future
        require(auctionTimestampEnd > block.timestamp, "Only future end time");

        // set auction details
        RAMProjectConfig_.timestampEnd = auctionTimestampEnd;
        // also update original end for accurate extra time calculation
        RAMProjectConfig_.timestampOriginalEnd = auctionTimestampEnd;

        // emit state change event
        emit AuctionTimestampEndUpdated({
            projectId: projectId,
            coreContract: coreContract,
            timestampEnd: auctionTimestampEnd
        });
    }

    /**
     * @notice Update the number of tokens in the auction, based on the state
     * of the core contract and the minter-local max invocations.
     * @param projectId Project ID to update
     * @param coreContract Core contract address to update
     */
    function refreshNumTokensInAuction(
        uint256 projectId,
        address coreContract
    ) internal returns (uint256 numTokensInAuction) {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // @dev safe to cast - max uint24 is 16_777_215 > 1_000_000 max project size
        numTokensInAuction = MaxInvocationsLib.getInvocationsAvailable({
            projectId: projectId,
            coreContract: coreContract
        });
        RAMProjectConfig_.numTokensInAuction = uint24(numTokensInAuction);

        // emit event for state change
        emit NumTokensInAuctionUpdated({
            projectId: projectId,
            coreContract: coreContract,
            numTokensInAuction: numTokensInAuction
        });
    }

    /**
     * @notice Collects settlement for project `projectId` on core contract
     * `coreContract` for all bids in `bidIds`.
     * Reverts if project is not in a post-auction state.
     * Reverts if bidder is not the bidder for all bids.
     * Reverts if one or more bids has already been settled.
     * Reverts if invalid bid is found.
     * @param projectId Project ID of bid to collect settlement for
     * @param coreContract Core contract address for the given project.
     * @param bidIds IDs of bids to collect settlements for
     * @param bidder Bidder address of bid to collect settlements for
     * @param minterRefundGasLimit Gas limit to use when refunding the bidder.
     */
    function collectSettlements(
        uint256 projectId,
        address coreContract,
        uint32[] calldata bidIds,
        address bidder,
        uint256 minterRefundGasLimit
    ) internal {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // CHECKS
        // @dev block scope to avoid stack too deep error
        {
            // require project minter state C or D (Post-Auction, Open Mint or Admin-Artist Mint Period)
            ProjectMinterStates projectMinterState = getProjectMinterState({
                projectId: projectId,
                coreContract: coreContract
            });
            require(
                projectMinterState ==
                    ProjectMinterStates.PostAuctionSellOutAdminArtistMint ||
                    projectMinterState ==
                    ProjectMinterStates.PostAuctionOpenMint,
                "Only state C or D"
            );
        }

        // get project price
        uint256 projectPrice = _getProjectPrice({
            RAMProjectConfig_: RAMProjectConfig_
        });

        // settle each input bid
        // @dev already verified that input lengths match
        uint256 inputBidsLength = bidIds.length;
        // @dev overflow check optimization as of 0.8.22
        for (uint256 i = 0; i < inputBidsLength; ++i) {
            // settle the bid
            _settleBidWithChecks({
                RAMProjectConfig_: RAMProjectConfig_,
                projectId: projectId,
                coreContract: coreContract,
                projectPrice: projectPrice,
                bidId: bidIds[i],
                bidder: bidder,
                minterRefundGasLimit: minterRefundGasLimit
            });
        }
    }

    /**
     * @notice Directly mint tokens to winners of project `projectId` on core
     * contract `coreContract`.
     * Does not guarantee an optimal ordering or handling of E1 state like
     * `adminArtistAutoMintTokensToWinners` does while in State C.
     * Skips over bids that have already been minted or refunded (front-running
     * protection)
     * Reverts if project is not in a post-auction state,
     * post-admin-artist-only mint period (i.e. State D), with tokens available
     * Reverts if bid does not exist at bidId.
     * Reverts if msg.sender is not the bidder for all bids if
     * requireSenderIsBidder is true.
     * @param projectId Project ID to mint tokens on.
     * @param coreContract Core contract address for the given project.
     * @param bidIds IDs of bids to mint tokens for
     * @param requireSenderIsBidder bool representing if the sender must be the
     * bidder for all bids
     * @param minterFilter Minter filter to use when minting tokens
     * @param minterRefundGasLimit Gas limit to use when refunding the bidder.
     */
    function directMintTokensToWinners(
        uint256 projectId,
        address coreContract,
        uint32[] calldata bidIds,
        bool requireSenderIsBidder,
        IMinterFilterV1 minterFilter,
        uint256 minterRefundGasLimit
    ) internal {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });

        // get project price
        uint256 projectPrice = _getProjectPrice({
            RAMProjectConfig_: RAMProjectConfig_
        });

        // CHECKS
        // @dev memoize length for gas efficiency
        uint256 bidIdsLength = bidIds.length;
        // @dev block scope to limit stack depth
        {
            // require project minter state D (Post-Auction,
            // post-admin-artist-only, not all bids handled)
            ProjectMinterStates projectMinterState = getProjectMinterState({
                projectId: projectId,
                coreContract: coreContract
            });
            require(
                projectMinterState == ProjectMinterStates.PostAuctionOpenMint,
                "Only post-auction open mint"
            );
            // require numTokensToMint does not exceed number of tokens
            // owed.
            // @dev must check this here to avoid minting more tokens than max
            // invocations, which could potentially not revert if minter
            // max invocations was limiting (+other unexpected conditions)
            require(
                bidIdsLength <=
                    _getNumTokensOwed({RAMProjectConfig_: RAMProjectConfig_}),
                "tokens to mint gt tokens owed"
            );
        }

        // main loop to mint tokens
        for (uint256 i; i < bidIdsLength; ++i) {
            // @dev current slot index and bid index in slot not memoized due
            // to stack depth limitations
            // get bid
            uint256 currentBidId = bidIds[i];
            Bid storage bid = RAMProjectConfig_.bids[currentBidId];
            address bidderAddress = bid.bidder;
            // CHECKS
            // require bid exists
            require(bidderAddress != address(0), "invalid Bid ID");
            // if bid is already minted or refunded, skip to next bid
            // @dev do not revert, since this could be due to front-running
            if (
                _getBidPackedBool(bid, INDEX_IS_MINTED) ||
                _getBidPackedBool(bid, INDEX_IS_REFUNDED)
            ) {
                continue;
            }
            // require sender is bidder if requireSenderIsBidder is true
            if (requireSenderIsBidder) {
                require(msg.sender == bidderAddress, "Only sender is bidder");
            }
            // EFFECTS
            // @dev num bids minted tokens not memoized due to stack depth
            // limitations
            RAMProjectConfig_.numBidsMintedTokens++;
            // Mint bid and settle if not already settled
            _mintAndSettle({
                projectId: projectId,
                coreContract: coreContract,
                projectPrice: projectPrice,
                slotIndex: bid.slotIndex,
                bidId: currentBidId,
                minterFilter: minterFilter,
                minterRefundGasLimit: minterRefundGasLimit
            });
        }
    }

    /**
     * @notice Function that enables a contract admin or artist (checked by
     * external function) to mint tokens to winners of project `projectId` on
     * core contract `coreContract`.
     * Automatically mints tokens to most-winning bids, in order from highest
     * and earliest bid to lowest and latest bid.
     * Settles bids as tokens are minted, if not already settled.
     * Reverts if project is not in a post-auction state, admin-artist-only mint
     * period (i.e. State C), with tokens available.
     * to be minted.
     * Reverts if number of tokens to mint is greater than the number of
     * tokens available to be minted.
     * @param projectId Project ID to mint tokens on.
     * @param coreContract Core contract address for the given project.
     * @param numTokensToMint Number of tokens to mint in this transaction.
     * @param minterFilter Minter filter contract address
     * @param minterRefundGasLimit Gas limit to use when settling bid if not already settled
     */
    function adminArtistAutoMintTokensToWinners(
        uint256 projectId,
        address coreContract,
        uint24 numTokensToMint,
        IMinterFilterV1 minterFilter,
        uint256 minterRefundGasLimit
    ) internal {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });

        // get project price
        uint256 projectPrice = _getProjectPrice({
            RAMProjectConfig_: RAMProjectConfig_
        });

        // CHECKS
        // @dev block scope to limit stack depth
        {
            // require project minter state C (Post-Auction, sell out, admin-artist-only,
            // not all bids handled)
            ProjectMinterStates projectMinterState = getProjectMinterState({
                projectId: projectId,
                coreContract: coreContract
            });
            require(
                projectMinterState ==
                    ProjectMinterStates.PostAuctionSellOutAdminArtistMint,
                "Only state C"
            );
            // require numTokensToMint does not exceed number of tokens
            // owed
            // @dev must check this here to avoid minting more tokens than max
            // invocations, which could potentially not revert if minter
            // max invocations was limiting (+other unexpected conditions)
            require(
                numTokensToMint <=
                    _getNumTokensOwed({RAMProjectConfig_: RAMProjectConfig_}),
                "tokens to mint gt tokens owed"
            );
        }

        // EFFECTS
        // load values to memory for gas efficiency
        uint256 currentLatestMintedBidId = RAMProjectConfig_.latestMintedBidId;
        // @dev will be zero if no bids minted yet
        uint256 currentLatestMintedBidSlotIndex = RAMProjectConfig_
            .bids[currentLatestMintedBidId]
            .slotIndex;

        uint256 numNewTokensMinted; // = 0

        // main loop to mint tokens
        while (numNewTokensMinted < numTokensToMint) {
            // EFFECTS
            // STEP 1: scroll to next bid to be minted a token
            // set latest minted bid indices to the bid to be minted a token
            if (currentLatestMintedBidId == 0) {
                // first mint, so need to initialize cursor values
                // set bid to highest bid in the project, head of max bid slot
                currentLatestMintedBidSlotIndex = RAMProjectConfig_
                    .maxBidSlotIndex;
                currentLatestMintedBidId = RAMProjectConfig_.headBidIdBySlot[
                    currentLatestMintedBidSlotIndex
                ];
            } else {
                // scroll to next bid in current slot
                // @dev scrolling to null is okay and handled below
                currentLatestMintedBidId = RAMProjectConfig_
                    .bids[currentLatestMintedBidId]
                    .nextBidId;
                // if scrolled off end of list, then find next slot with bids
                if (currentLatestMintedBidId == 0) {
                    // past tail of current slot's linked list, so need to find next
                    // bid slot with bids
                    currentLatestMintedBidSlotIndex = _getMaxSlotWithBid({
                        RAMProjectConfig_: RAMProjectConfig_,
                        startSlotIndex: uint16(
                            currentLatestMintedBidSlotIndex - 1
                        )
                    });
                    // @dev no coverage on else branch because it is unreachable as used
                    require(
                        currentLatestMintedBidSlotIndex < NUM_SLOTS,
                        "slot with bid not found"
                    );
                    // current bid is now the head of the linked list
                    currentLatestMintedBidId = RAMProjectConfig_
                        .headBidIdBySlot[currentLatestMintedBidSlotIndex];
                }
            }

            // @dev minter is in State C, so bid must not have been minted or
            // refunded due to scrolling logic of admin mint and refund
            // functions available for use while in State C. The bid may have
            // been previously settled, however.

            // Mint bid and settle if not already settled
            // @dev scrolling logic in State C ensures bid **exists** is not yet minted
            _mintAndSettle({
                projectId: projectId,
                coreContract: coreContract,
                projectPrice: projectPrice,
                slotIndex: currentLatestMintedBidSlotIndex,
                bidId: currentLatestMintedBidId,
                minterFilter: minterFilter,
                minterRefundGasLimit: minterRefundGasLimit
            });

            // increment num new tokens minted
            unchecked {
                ++numNewTokensMinted;
            }
        }

        // finally, update auction metadata storage state from memoized values
        // @dev safe to cast numNewTokensMinted to uint24
        RAMProjectConfig_.numBidsMintedTokens += uint24(numNewTokensMinted);
        // @dev safe to cast to uint32 because directly derived from bid ID
        RAMProjectConfig_.latestMintedBidId = uint32(currentLatestMintedBidId);
    }

    /**
     * @notice Directly refund bids for project `projectId` on core contract
     * `coreContract` to resolve error state E1.
     * Does not guarantee an optimal ordering or handling of E1 state like
     * `autoRefundBidsToResolveE1` does while in State C.
     * Skips over bids that have already been minted or refunded (front-running
     * protection)
     * Reverts if project is not in post-auction state,
     * post-admin-artist-only mint period (i.e. State D).
     * Reverts if project is not in error state E1.
     * Reverts if length of bids to refund exceeds the number of bids that need
     * to be refunded to resolve the error state E1.
     * Reverts if bid does not exist at bidId.
     * Reverts if msg.sender is not the bidder for all bids if
     * requireSenderIsBidder is true.
     * @param projectId Project ID to refunds bids for.
     * @param coreContract Core contract address for the given project.
     * @param bidIds IDs of bids to refund bid values for
     * @param requireSenderIsBidder Require sender is bidder for all bids
     * @param minterRefundGasLimit Gas limit to use when refunding the bidder.
     */
    function directRefundBidsToResolveE1(
        uint256 projectId,
        address coreContract,
        uint32[] calldata bidIds,
        bool requireSenderIsBidder,
        uint256 minterRefundGasLimit
    ) internal {
        // CHECKS
        // @dev memoize length for gas efficiency
        uint256 bidIdsLength = bidIds.length;
        // @dev block scope to limit stack depth
        {
            // require project minter state D (Post-Auction, post-admin-artist-only,
            // not all bids handled)
            ProjectMinterStates projectMinterState = getProjectMinterState({
                projectId: projectId,
                coreContract: coreContract
            });
            require(
                projectMinterState == ProjectMinterStates.PostAuctionOpenMint,
                "Only post-auction open mint"
            );
            // require is in state E1
            (bool isErrorE1_, uint256 numBidsToResolveE1, ) = isErrorE1FlagF1({
                projectId: projectId,
                coreContract: coreContract
            });
            require(isErrorE1_, "Only in state E1");
            // require numBidsToRefund does not exceed max number of bids
            // to resolve E1 error state
            require(
                bidIdsLength <= numBidsToResolveE1,
                "bids to refund gt available qty"
            );
        }

        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // get project price
        uint256 projectPrice = _getProjectPrice({
            RAMProjectConfig_: RAMProjectConfig_
        });

        // @dev memoize for gas efficiency
        uint24 numRefundsIssued = 0;
        // main loop to refund tokens
        for (uint256 i; i < bidIdsLength; ++i) {
            // @dev current slot index and bid index in slot not memoized due
            // to stack depth limitations
            // get bid
            uint256 currentBidId = bidIds[i];
            Bid storage bid = RAMProjectConfig_.bids[currentBidId];
            // CHECKS
            // require bidder is non-zero address (i.e. bid exists)
            address bidderAddress = bid.bidder;
            require(bidderAddress != address(0), "invalid Bid ID");
            // if bid is already minted or refunded, skip to next bid
            // @dev do not revert, since this could be due to front-running
            if (
                _getBidPackedBool(bid, INDEX_IS_MINTED) ||
                _getBidPackedBool(bid, INDEX_IS_REFUNDED)
            ) {
                continue;
            }
            // require sender is bidder if requireSenderIsBidder is true
            if (requireSenderIsBidder) {
                require(msg.sender == bidderAddress, "Only sender is bidder");
            }
            // EFFECTS
            // Settle and Refund the Bid
            _settleAndRefundBid({
                projectId: projectId,
                coreContract: coreContract,
                projectPrice: projectPrice,
                slotIndex: bid.slotIndex,
                bidId: currentBidId,
                minterRefundGasLimit: minterRefundGasLimit
            });
            numRefundsIssued++;
        }
        // update number of bids refunded
        RAMProjectConfig_.numBidsErrorRefunded += numRefundsIssued;
    }

    /**
     * @notice Function to automatically refund the lowest winning bids for
     * project `projectId` on core contract `coreContract` to resolve error
     * state E1.
     * Reverts if project is not in post-auction state C.
     * Reverts if project is not in error state E1.
     * Reverts if numBidsToRefund exceeds the number of bids that need to be
     * refunded to resolve the error state E1.
     * @dev Recommend admin-only not for security, but rather to enable Admin
     * to be aware that an error state has been encountered while in post-
     * auction state C.
     * @param projectId Project ID to refunds bids for.
     * @param coreContract Core contract address for the given project.
     * @param numBidsToRefund Number of bids to refund in this call.
     * @param minterRefundGasLimit Gas limit to use when refunding the bidder.
     */
    function autoRefundBidsToResolveE1(
        uint256 projectId,
        address coreContract,
        uint24 numBidsToRefund,
        uint256 minterRefundGasLimit
    ) internal {
        // CHECKS
        // @dev block scope to limit stack depth
        {
            // require project minter state C (Post-Auction, admin-artist-only,
            //  not all bids handled)
            ProjectMinterStates projectMinterState = getProjectMinterState({
                projectId: projectId,
                coreContract: coreContract
            });
            require(
                projectMinterState ==
                    ProjectMinterStates.PostAuctionSellOutAdminArtistMint,
                "Only state C"
            );
            // require is in state E1
            (bool isErrorE1_, uint256 numBidsToResolveE1, ) = isErrorE1FlagF1({
                projectId: projectId,
                coreContract: coreContract
            });
            require(isErrorE1_, "Only in state E1");
            // require numBidsToRefund does not exceed max number of bids
            // to resolve E1 error state
            require(
                numBidsToRefund <= numBidsToResolveE1,
                "bids to refund gt available qty"
            );
        }
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // get project price
        uint256 projectPrice = _getProjectPrice({
            RAMProjectConfig_: RAMProjectConfig_
        });

        // EFFECTS
        // load values to memory for gas efficiency
        uint256 currentLatestRefundedBidId = RAMProjectConfig_
            .latestRefundedBidId;
        uint256 currentLatestRefundedBidSlotIndex = RAMProjectConfig_
            .bids[currentLatestRefundedBidId]
            .slotIndex;
        // settlement values
        uint256 numRefundsIssued; // = 0

        // main loop to refund bids
        while (numRefundsIssued < numBidsToRefund) {
            // EFFECTS
            // STEP 1: Get next bid to be refunded
            // set latest refunded bid indices to the bid to be refunded
            if (currentLatestRefundedBidId == 0) {
                // first refund, so need to initialize cursor values
                // set bid to lowest bid in the project, tail of min bid slot
                currentLatestRefundedBidSlotIndex = RAMProjectConfig_
                    .minBidSlotIndex;
                currentLatestRefundedBidId = RAMProjectConfig_.tailBidIdBySlot[
                    currentLatestRefundedBidSlotIndex
                ];
            } else {
                // scroll to previous bid in current slot
                // @dev scrolling to null is okay and handled below
                currentLatestRefundedBidId = RAMProjectConfig_
                    .bids[currentLatestRefundedBidId]
                    .prevBidId;
            }

            // if scrolled off end of list, then find next slot with bids
            if (currentLatestRefundedBidId == 0) {
                // past head of current slot's linked list, so need to find next
                // bid slot with bids
                // @dev not possible to not find next slot during auto-refund,
                // so no need to handle case where slot not found
                currentLatestRefundedBidSlotIndex = _getMinSlotWithBid({
                    RAMProjectConfig_: RAMProjectConfig_,
                    startSlotIndex: uint16(
                        currentLatestRefundedBidSlotIndex + 1
                    )
                });
                // current bid is now the tail of the linked list
                currentLatestRefundedBidId = RAMProjectConfig_.tailBidIdBySlot[
                    currentLatestRefundedBidSlotIndex
                ];
            }

            // @dev minter is in State C, so bid must not have been minted or
            // refunded due to scrolling logic of admin mint and refund
            // functions available for use while in State C. The bid may have
            // been previously settled, however.

            // Settle & Refund the Bid
            _settleAndRefundBid({
                projectId: projectId,
                coreContract: coreContract,
                projectPrice: projectPrice,
                slotIndex: uint16(currentLatestRefundedBidSlotIndex),
                bidId: currentLatestRefundedBidId,
                minterRefundGasLimit: minterRefundGasLimit
            });
            // increment loop counter and current num bids refunded
            unchecked {
                ++numRefundsIssued;
            }
        }

        // finally, update auction metadata storage state from memoized values
        // @dev safe to cast currentNumBidsErrorRefunded to uint24
        RAMProjectConfig_.numBidsErrorRefunded += uint24(numRefundsIssued);
        // @dev safe to cast to uint32 because directly derived from bid ID
        RAMProjectConfig_.latestRefundedBidId = uint32(
            currentLatestRefundedBidId
        );
    }

    /**
     * @notice This withdraws project revenues for project `projectId` on core
     * contract `coreContract` to the artist and admin, only after all bids
     * have been minted+settled or refunded.
     * Note that the conditions described are the equivalent of project minter
     * State E.
     * @param projectId Project ID to withdraw revenues for.
     * @param coreContract Core contract address for the given project.
     */
    function withdrawArtistAndAdminRevenues(
        uint256 projectId,
        address coreContract
    ) internal {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });

        // CHECKS
        // require project minter state E (Post-Auction, all bids handled)
        ProjectMinterStates projectMinterState = getProjectMinterState({
            projectId: projectId,
            coreContract: coreContract
        });
        require(
            projectMinterState == ProjectMinterStates.PostAuctionAllBidsHandled,
            "Only state E"
        );
        // require revenues not already withdrawn
        require(
            !(RAMProjectConfig_.revenuesCollected),
            "Revenues already withdrawn"
        );

        // EFFECTS
        // update state to indicate revenues withdrawn
        RAMProjectConfig_.revenuesCollected = true;

        // get project price
        uint256 projectPrice = _getProjectPrice({
            RAMProjectConfig_: RAMProjectConfig_
        });
        // get netRevenues
        // @dev refunded bids do not count towards amount due because they
        // did not generate revenue
        uint256 netRevenues = projectPrice *
            RAMProjectConfig_.numBidsMintedTokens;

        // update project balance
        // @dev reverts on underflow
        RAMProjectConfig_.projectBalance -= uint120(netRevenues);

        // INTERACTIONS
        SplitFundsLib.splitRevenuesETHNoRefund({
            projectId: projectId,
            valueInWei: netRevenues,
            coreContract: coreContract
        });

        emit GenericMinterEventsLib.ConfigValueSet({
            projectId: projectId,
            coreContract: coreContract,
            key: CONFIG_AUCTION_REVENUES_COLLECTED,
            value: true
        });
    }

    /**
     * @notice Function to mint tokens if an auction is over, but did not sell
     * out and tokens are still available to be minted.
     * @dev must be called within non-reentrant context
     * @param to Address to be the new token's owner.
     * @param projectId Project ID to mint a token on.
     * @param coreContract Core contract address for the given project.
     * @param minterFilter Minter filter to use when minting token.
     * @return tokenId Token ID of minted token
     */
    function purchaseTo(
        address to,
        uint256 projectId,
        address coreContract,
        IMinterFilterV1 minterFilter
    ) internal returns (uint256 tokenId) {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });

        // CHECKS
        // @dev block scope to limit stack depth
        {
            // require project minter state D, or E (Post-Auction)
            ProjectMinterStates projectMinterState = getProjectMinterState({
                projectId: projectId,
                coreContract: coreContract
            });
            require(
                projectMinterState == ProjectMinterStates.PostAuctionOpenMint ||
                    projectMinterState ==
                    ProjectMinterStates.PostAuctionAllBidsHandled,
                "Only state D or E"
            );
            // require Flag F1, i.e. at least one excess token available to be
            // minted
            // @dev this ensures minter and core contract max-invocations
            // constraints are not violated, as well as confirms that one
            // additional mint will not send the minter into an E1 state
            (, , uint256 numExcessInvocationsAvailable) = isErrorE1FlagF1({
                projectId: projectId,
                coreContract: coreContract
            });
            require(
                numExcessInvocationsAvailable > 0,
                "Reached max invocations"
            );
        }
        // require sufficient payment
        // since excess invocations are available, know not a sellout, so
        // project price is base price
        uint256 pricePerTokenInWei = RAMProjectConfig_.basePrice;
        require(
            msg.value == pricePerTokenInWei,
            "Only send auction reserve price"
        );

        // EFFECTS
        // mint token
        tokenId = minterFilter.mint_joo({
            to: to,
            projectId: projectId,
            coreContract: coreContract,
            sender: msg.sender
        });

        // @dev this minter specifically does not update max invocations has
        // been reached, since it must consider unminted bids when determining
        // if max invocations has been reached

        // INTERACTIONS
        // split revenue from sale
        // @dev no refund because previously verified msg.value == pricePerTokenInWei
        // @dev no effect on project balance, splitting same amount received
        SplitFundsLib.splitRevenuesETHNoRefund({
            projectId: projectId,
            valueInWei: pricePerTokenInWei,
            coreContract: coreContract
        });

        // emit event for state change
        emit TokenPurchased({
            projectId: projectId,
            coreContract: coreContract,
            tokenId: tokenId,
            to: to
        });
    }

    /**
     * @notice Place a new bid for a project.
     * Assumes check that minter is set for project on minter filter has
     * already been performed.
     * Reverts if project is not in state B (Live Auction).
     * Reverts if bid value is not equal to the slot value.
     * @param projectId Project Id to place bid for
     * @param coreContract Core contract address to place bid for
     * @param slotIndex Slot index to place bid at
     * @param bidder Bidder address
     * @param bidValue Bid value, in Wei (verified to align with slotIndex)
     * @param minterRefundGasLimit Gas limit to use when refunding the previous
     * highest bidder, prior to using fallback force-send to refund
     */
    function placeBid(
        uint256 projectId,
        address coreContract,
        uint16 slotIndex,
        address bidder,
        uint256 bidValue,
        uint256 minterRefundGasLimit
    ) internal {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // CHECKS
        // require project minter state B (Live Auction)
        require(
            getProjectMinterState(projectId, coreContract) ==
                ProjectMinterStates.LiveAuction,
            "Only live auction"
        );
        // require slot index not out of range
        // @dev slot index out of range is checked in slotIndexToBidValue
        // require bid value must equal slot value
        uint256 newBidRequiredValue = slotIndexToBidValue({
            basePrice: RAMProjectConfig_.basePrice,
            slotIndex: slotIndex
        });
        require(
            bidValue == newBidRequiredValue,
            "msg.value must equal slot value"
        );

        // EFFECTS
        // add bid value to project balance
        RAMProjectConfig_.projectBalance += uint120(bidValue);
        // if first bid, refresh max invocations in case artist has reduced
        // the core contract's max invocations after the auction was configured
        // @dev this helps prevent E1 error state
        if (RAMProjectConfig_.numBids == 0) {
            // refresh max invocations
            MaxInvocationsLib.refreshMaxInvocations({
                projectId: projectId,
                coreContract: coreContract
            });
            // also refresh numTokensInAuction for RAM project config
            refreshNumTokensInAuction({
                projectId: projectId,
                coreContract: coreContract
            });
        }
        // require at least one token allowed in auction
        // @dev this case would revert in _removeMinBid, but prefer clean error
        // message here
        uint256 numTokensInAuction = RAMProjectConfig_.numTokensInAuction;
        require(numTokensInAuction > 0, "No tokens in auction");
        // determine if have reached max bids
        bool reachedMaxBids = RAMProjectConfig_.numBids >= numTokensInAuction;
        if (reachedMaxBids) {
            // remove + refund the minimum Bid
            uint256 removedBidValue = _removeMinBid({
                RAMProjectConfig_: RAMProjectConfig_,
                projectId: projectId,
                coreContract: coreContract,
                minterRefundGasLimit: minterRefundGasLimit
            });
            // require new bid is sufficiently greater than removed minimum bid
            require(
                _isSufficientOutbid({
                    oldBidValue: removedBidValue,
                    newBidValue: bidValue
                }),
                "Insufficient bid value"
            );

            // apply auction extension time if needed
            bool timeExtensionNeeded = RAMProjectConfig_.allowExtraTime &&
                block.timestamp >
                RAMProjectConfig_.timestampEnd - AUCTION_BUFFER_SECONDS;
            if (timeExtensionNeeded) {
                // extend auction end time to no longer than
                // MAX_AUCTION_EXTRA_SECONDS after original end time
                RAMProjectConfig_.timestampEnd = uint40(
                    Math.min(
                        RAMProjectConfig_.timestampOriginalEnd +
                            MAX_AUCTION_EXTRA_SECONDS,
                        block.timestamp + AUCTION_BUFFER_SECONDS
                    )
                );
                emit AuctionTimestampEndUpdated({
                    projectId: projectId,
                    coreContract: coreContract,
                    timestampEnd: RAMProjectConfig_.timestampEnd
                });
            }
        }
        // insert the new Bid
        _insertBid({
            RAMProjectConfig_: RAMProjectConfig_,
            projectId: projectId,
            coreContract: coreContract,
            slotIndex: slotIndex,
            bidder: bidder,
            bidId: 0 // zero triggers new bid ID to be assigned
        });
    }

    /**
     * @notice Top up bid for project `projectId` on core contract
     * `coreContract` for bid `bidId` to new slot index `newSlotIndex`.
     * Reverts if Bid ID has been kicked out of the auction or does not exist.
     * Reverts if bidder is not the bidder of the bid.
     * Reverts if project is not in a Live Auction.
     * Reverts if addedValue is not equal to difference in bid values between
     * new and old slots.
     * Reverts if new slot index is not greater than or equal to the current
     * slot index.
     * @param projectId Project ID to top up bid for.
     * @param coreContract Core contract address for the given project.
     * @param bidId ID of bid to top up.
     * @param newSlotIndex New slot index to move bid to.
     * @param bidder Bidder address
     * @param addedValue Value to add to the bid, in Wei
     */
    function topUpBid(
        uint256 projectId,
        address coreContract,
        uint32 bidId,
        uint16 newSlotIndex,
        address bidder,
        uint256 addedValue
    ) internal {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        Bid storage bid = RAMProjectConfig_.bids[bidId];
        // memoize for gas efficiency
        uint16 oldSlotIndex = bid.slotIndex;
        // CHECKS
        {
            // require project minter state B (Live Auction)
            require(
                getProjectMinterState(projectId, coreContract) ==
                    ProjectMinterStates.LiveAuction,
                "Only live auction"
            );
            // require new slot index not out of range
            // @dev slot index out of range is checked in slotIndexToBidValue
            // @dev give clean error message if bid is null or deleted
            require(bid.bidder != address(0), "Bid dne - were you outbid?");
            // require bidder owns referenced bid
            require(bid.bidder == bidder, "Only bidder of existing bid");
            // require correct added bid value
            uint256 oldBidValue = slotIndexToBidValue({
                basePrice: RAMProjectConfig_.basePrice,
                slotIndex: oldSlotIndex
            });
            uint256 newBidValue = slotIndexToBidValue({
                basePrice: RAMProjectConfig_.basePrice,
                slotIndex: newSlotIndex
            });
            // implicitly checks that newSlotIndex > oldSlotIndex, since
            // addedValue must be positive
            require(
                oldBidValue + addedValue == newBidValue,
                "incorrect added value"
            );
        }

        // EFFECTS
        // add the added value to project balance
        RAMProjectConfig_.projectBalance += uint120(addedValue);
        // eject bid from the linked list at oldSlotIndex
        _ejectBidFromSlot({
            RAMProjectConfig_: RAMProjectConfig_,
            slotIndex: oldSlotIndex,
            bidId: bidId
        });
        // insert the existing bid into newSlotIndex's linked list
        _insertBid({
            RAMProjectConfig_: RAMProjectConfig_,
            projectId: projectId,
            coreContract: coreContract,
            slotIndex: newSlotIndex,
            bidder: bidder,
            bidId: bidId
        });

        // emit top-up event
        emit BidToppedUp({
            projectId: projectId,
            coreContract: coreContract,
            bidId: bidId,
            newSlotIndex: newSlotIndex
        });
    }

    /**
     * @notice Returns a storage pointer to the Bid struct and slot index of the lowest bid in the
     * project's auction, in Wei.
     * Reverts if no bids exist in the auction.
     * @param projectId Project ID to get the lowest bid value for
     * @param coreContract Core contract address for the given project
     * @return minBid Storage pointer to Bid struct of the lowest bid in
     * the auction
     * @return minSlotIndex Slot index of the lowest bid in the auction
     */
    function getLowestBid(
        uint256 projectId,
        address coreContract
    ) internal view returns (Bid storage minBid, uint16 minSlotIndex) {
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // revert if no bids in auction
        require(RAMProjectConfig_.numBids > 0, "No bids in auction");
        // get min slot with a bid
        minSlotIndex = RAMProjectConfig_.minBidSlotIndex;
        // get the tail bid ID for the min slot
        uint256 tailBidId = RAMProjectConfig_.tailBidIdBySlot[minSlotIndex];
        minBid = RAMProjectConfig_.bids[tailBidId];
    }

    /**
     * @notice Returns the auction details for project `projectId` on core
     * contract `coreContract`.
     * @param projectId is an existing project ID.
     * @param coreContract is an existing core contract address.
     * @return auctionTimestampStart is the timestamp at which the auction
     * starts.
     * @return auctionTimestampEnd is the timestamp at which the auction ends.
     * @return basePrice is the resting price of the auction, in Wei.
     * @return numTokensInAuction is the number of tokens in the auction.
     * @return numBids is the number of bids in the auction.
     * @return numBidsMintedTokens is the number of bids that have been minted
     * into tokens.
     * @return numBidsErrorRefunded is the number of bids that have been
     * refunded due to an error state.
     * @return minBidSlotIndex is the index of the slot with the minimum bid
     * value.
     * @return allowExtraTime is a bool indicating if the auction is allowed to
     * have extra time.
     * @return adminArtistOnlyMintPeriodIfSellout is a bool indicating if an
     * admin-artist-only mint period is required if the auction sells out.
     * @return revenuesCollected is a bool indicating if the auction revenues
     * have been collected.
     * @return projectMinterState is the current state of the project minter.
     * @dev projectMinterState is a RAMLib.ProjectMinterStates enum value.
     */
    function getAuctionDetails(
        uint256 projectId,
        address coreContract
    )
        internal
        view
        returns (
            uint256 auctionTimestampStart,
            uint256 auctionTimestampEnd,
            uint256 basePrice,
            uint256 numTokensInAuction,
            uint256 numBids,
            uint256 numBidsMintedTokens,
            uint256 numBidsErrorRefunded,
            uint256 minBidSlotIndex,
            bool allowExtraTime,
            bool adminArtistOnlyMintPeriodIfSellout,
            bool revenuesCollected,
            RAMLib.ProjectMinterStates projectMinterState
        )
    {
        // asign project minter state
        projectMinterState = getProjectMinterState({
            projectId: projectId,
            coreContract: coreContract
        });
        // get project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // get auction details
        auctionTimestampStart = RAMProjectConfig_.timestampStart;
        auctionTimestampEnd = RAMProjectConfig_.timestampEnd;
        basePrice = RAMProjectConfig_.basePrice;
        numTokensInAuction = RAMProjectConfig_.numTokensInAuction;
        numBids = RAMProjectConfig_.numBids;
        numBidsMintedTokens = RAMProjectConfig_.numBidsMintedTokens;
        numBidsErrorRefunded = RAMProjectConfig_.numBidsErrorRefunded;
        minBidSlotIndex = RAMProjectConfig_.minBidSlotIndex;
        allowExtraTime = RAMProjectConfig_.allowExtraTime;
        adminArtistOnlyMintPeriodIfSellout = RAMProjectConfig_
            .adminArtistOnlyMintPeriodIfSellout;
        revenuesCollected = RAMProjectConfig_.revenuesCollected;
    }

    /**
     * @notice Returns the price information for a given project.
     * If an auction is not configured, `isConfigured` will be false, and a
     * dummy price of zero is assigned to `tokenPriceInWei`.
     * If an auction is configured but still in a pre-auction state,
     * `isConfigured` will be true, and `tokenPriceInWei` will be the minimum
     * initial bid price for the next token auction.
     * If there is an active auction, `isConfigured` will be true, and
     * `tokenPriceInWei` will be the current minimum bid's value + min bid
     * increment due to the minter's increment percentage, rounded up to next
     * slot's bid value.
     * If there is an auction that has ended (no longer accepting bids), but
     * the project is configured, `isConfigured` will be true, and
     * `tokenPriceInWei` will be either the sellout price or the reserve price
     * of the auction if it did not sell out during its auction.
     * @param projectId Project ID to get price information for
     * @param coreContract Core contract address for the given project
     * @return isConfigured True if the project is configured, false otherwise
     * @return tokenPriceInWei Price of a token in Wei, if configured
     */
    function getPriceInfo(
        uint256 projectId,
        address coreContract
    ) internal view returns (bool isConfigured, uint256 tokenPriceInWei) {
        // get minter state
        RAMLib.ProjectMinterStates projectMinterState = getProjectMinterState({
            projectId: projectId,
            coreContract: coreContract
        });
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // handle pre-auction State A
        if (projectMinterState == RAMLib.ProjectMinterStates.PreAuction) {
            isConfigured = RAMProjectConfig_.timestampStart > 0;
            // if not configured, leave tokenPriceInWei as 0
            if (isConfigured) {
                tokenPriceInWei = RAMProjectConfig_.basePrice;
            }
        } else {
            // values that apply to all live-auction and post-auction states
            isConfigured = true;
            bool isSellout = RAMProjectConfig_.numBids >=
                RAMProjectConfig_.numTokensInAuction;

            // handle live-auction State B
            if (projectMinterState == RAMLib.ProjectMinterStates.LiveAuction) {
                if (isSellout) {
                    // find next valid bid value
                    // @dev okay if we extend past the maximum slot index value
                    // for this view function
                    (, tokenPriceInWei) = _findNextValidBidSlotIndexAndValue({
                        projectId: projectId,
                        coreContract: coreContract,
                        startSlotIndex: RAMProjectConfig_.minBidSlotIndex
                    });
                } else {
                    // not sellout, so min bid is base price
                    tokenPriceInWei = RAMProjectConfig_.basePrice;
                }
            } else {
                // handle post-auction States C, D, E
                if (isSellout) {
                    // if sellout, return min bid price
                    tokenPriceInWei = slotIndexToBidValue({
                        basePrice: RAMProjectConfig_.basePrice,
                        slotIndex: RAMProjectConfig_.minBidSlotIndex
                    });
                } else {
                    // not sellout, so return base price
                    tokenPriceInWei = RAMProjectConfig_.basePrice;
                }
            }
        }
    }

    /**
     * @notice Gets minimum next bid value in Wei and slot index for project `projectId`
     * on core contract `coreContract`.
     * If in a pre-auction state, reverts if unconfigured, otherwise returns
     * the minimum initial bid price for the upcoming auction.
     * If in an active auction, returns the minimum next bid's value and slot
     * index.
     * If in a post-auction state, reverts if auction was a sellout, otherwise
     * returns the auction's reserve price and slot index 0 (because tokens may
     * still be purchasable at the reserve price).
     * @param projectId Project ID to get the minimum next bid value for
     * @param coreContract Core contract address for the given project
     * @return minNextBidValueInWei minimum next bid value in Wei
     * @return minNextBidSlotIndex slot index of the minimum next bid
     */
    function getMinimumNextBid(
        uint256 projectId,
        address coreContract
    )
        internal
        view
        returns (uint256 minNextBidValueInWei, uint256 minNextBidSlotIndex)
    {
        // get minter state
        RAMLib.ProjectMinterStates projectMinterState = getProjectMinterState({
            projectId: projectId,
            coreContract: coreContract
        });
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // handle pre-auction State A
        if (projectMinterState == RAMLib.ProjectMinterStates.PreAuction) {
            bool isConfigured = RAMProjectConfig_.timestampStart > 0;
            if (!isConfigured) {
                // if not configured, revert
                revert("auction not configured");
            }
            // if configured, min next bid is base price at slot 0
            minNextBidValueInWei = RAMProjectConfig_.basePrice;
            minNextBidSlotIndex = 0;
        } else {
            // values that apply to all live-auction and post-auction states
            bool isSellout = RAMProjectConfig_.numBids >=
                RAMProjectConfig_.numTokensInAuction;

            // handle live-auction State B
            if (projectMinterState == RAMLib.ProjectMinterStates.LiveAuction) {
                if (isSellout) {
                    // find next valid bid slot index and value
                    // @dev okay if we extend past the maximum slot index and value
                    // for this view function
                    (
                        minNextBidSlotIndex,
                        minNextBidValueInWei
                    ) = _findNextValidBidSlotIndexAndValue({
                        projectId: projectId,
                        coreContract: coreContract,
                        startSlotIndex: RAMProjectConfig_.minBidSlotIndex
                    });
                } else {
                    // not sellout, so min bid is base price
                    minNextBidValueInWei = RAMProjectConfig_.basePrice;
                    minNextBidSlotIndex = 0;
                }
            } else {
                // handle post-auction States C, D, E
                if (isSellout) {
                    // if sellout, revert
                    revert("auction ended, sellout");
                } else {
                    // not sellout, so return base price
                    minNextBidValueInWei = RAMProjectConfig_.basePrice;
                    minNextBidSlotIndex = 0;
                }
            }
        }
    }

    /**
     * @notice Gets the project minter state of project `projectId` on core
     * contract `coreContract`.
     * @param projectId Project ID to get the minimum next bid value for
     * @param coreContract Core contract address for the given project
     * @return ProjectMinterStates enum representing the minter state.
     */
    function getProjectMinterState(
        uint256 projectId,
        address coreContract
    ) internal view returns (ProjectMinterStates) {
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // State A: Pre-Auction
        // @dev load to memory for gas efficiency
        uint256 timestampStart = RAMProjectConfig_.timestampStart;
        // helper value(s) for readability
        bool auctionIsConfigured = timestampStart > 0;
        bool isPreAuction = block.timestamp < timestampStart;
        // confirm that auction is either not configured or is pre-auction
        if ((!auctionIsConfigured) || isPreAuction) {
            return ProjectMinterStates.PreAuction;
        }
        // State B: Live-Auction
        // @dev auction is configured due to previous State A return
        // helper value(s) for readability
        // @dev load to memory for gas efficiency
        uint256 timestampEnd = RAMProjectConfig_.timestampEnd;
        bool isPostAuction = block.timestamp > timestampEnd;
        // pre-auction is checked above
        if (!isPostAuction) {
            return ProjectMinterStates.LiveAuction;
        }
        // States C, D, E: Post-Auction
        // @dev auction is configured and post auction due to previous States A, B returns
        // all winners sent tokens means all bids have either been minted tokens or refunded if error state occurred
        bool allBidsHandled = RAMProjectConfig_.numBidsMintedTokens +
            RAMProjectConfig_.numBidsErrorRefunded ==
            RAMProjectConfig_.numBids;
        if (allBidsHandled) {
            // State E: Post-Auction, all bids handled
            return ProjectMinterStates.PostAuctionAllBidsHandled;
        }
        // @dev all bids are not handled due to previous State E return
        bool adminOnlyMintPeriod = RAMProjectConfig_
        // @dev if project is configured to have an admin-artist-only mint period
            .adminArtistOnlyMintPeriodIfSellout &&
            // @dev sellout if numBids >= numTokensInAuction
            RAMProjectConfig_.numBids >= RAMProjectConfig_.numTokensInAuction &&
            // @dev still in admin-artist-only mint period if current time < end time + admin-artist-only mint period
            block.timestamp <
            timestampEnd + ADMIN_ARTIST_ONLY_MINT_TIME_SECONDS;
        if (adminOnlyMintPeriod) {
            // State C: Post-Auction, sell out, not all bids handled, admin-artist-only mint period
            return ProjectMinterStates.PostAuctionSellOutAdminArtistMint;
        }
        // State D: Post-Auction, not all bids handled, post-admin-artist-only mint period
        // @dev states are mutually exclusive, so must be in final remaining state
        return ProjectMinterStates.PostAuctionOpenMint;
    }

    /**
     * @notice Returns if project minter is in ERROR state E1, and the number
     * of bids that need to be refunded to resolve the error. Also returns the
     * number of excess invocations available, if any, indicating Flag F1.
     * E1: Tokens owed > invocations available
     * Occurs when: tokens are minted on different minter after auction begins,
     * or when core contract max invocations are reduced after auction begins.
     * Resolution: Admin must refund the lowest bids after auction ends.
     * @param projectId Project Id to query
     * @param coreContract Core contract address to query
     * @return isError True if in error state, false otherwise
     * @return numBidsToRefund Number of bids to refund to resolve error, 0 if
     * not in error state
     * @return numExcessInvocationsAvailable Number of excess invocations
     * available. Value above 0 indicates Flag F1.
     */
    function isErrorE1FlagF1(
        uint256 projectId,
        address coreContract
    )
        internal
        view
        returns (
            bool isError,
            uint256 numBidsToRefund,
            uint256 numExcessInvocationsAvailable
        )
    {
        // get project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // E1: Tokens owed > invocations available
        uint256 tokensOwed = _getNumTokensOwed({
            RAMProjectConfig_: RAMProjectConfig_
        });
        uint256 invocationsAvailable = MaxInvocationsLib
            .getInvocationsAvailable({
                projectId: projectId,
                coreContract: coreContract
            });
        // populate return values
        isError = tokensOwed > invocationsAvailable;
        numBidsToRefund = isError ? tokensOwed - invocationsAvailable : 0;
        // no excess invocations available if in error state, otherwise is the
        // difference between invocations available and tokens owed
        numExcessInvocationsAvailable = isError
            ? 0
            : invocationsAvailable - tokensOwed;
    }

    /**
     * @notice Returns the MaxInvocationsProjectConfig for a given project and
     * core contract, properly accounting for the auction state, unminted bids,
     * core contract invocations, and minter max invocations when determining
     * maxHasBeenInvoked
     * @param projectId Project Id to get config for
     * @param coreContract Core contract address to get config for
     * @return maxInvocationsProjectConfig max invocations project configuration
     */
    function getMaxInvocationsProjectConfig(
        uint256 projectId,
        address coreContract
    )
        internal
        view
        returns (
            MaxInvocationsLib.MaxInvocationsProjectConfig
                memory maxInvocationsProjectConfig
        )
    {
        // get max invocations project config from MaxInvocationsLib
        maxInvocationsProjectConfig.maxInvocations = uint24(
            MaxInvocationsLib.getMaxInvocations({
                projectId: projectId,
                coreContract: coreContract
            })
        );
        maxInvocationsProjectConfig.maxHasBeenInvoked = getMaxHasBeenInvoked({
            projectId: projectId,
            coreContract: coreContract
        });
    }

    /**
     * @notice Returns if project has reached maximum number of invocations for
     * a given project and core contract, properly accounting for the auction
     * state, unminted bids, core contract invocations, and minter max
     * invocations when determining maxHasBeenInvoked
     * @param projectId Project Id to get config for
     * @param coreContract Core contract address to get config for
     * @return maxHasBeenInvoked bool indicating if max invocations have been invoked
     */
    function getMaxHasBeenInvoked(
        uint256 projectId,
        address coreContract
    ) internal view returns (bool maxHasBeenInvoked) {
        // calculate if max has been invoked based on auction state
        ProjectMinterStates projectMinterState = getProjectMinterState({
            projectId: projectId,
            coreContract: coreContract
        });
        if (projectMinterState == ProjectMinterStates.PreAuction) {
            // pre-auction, true if numTokensInAuction == 0
            RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
            if (RAMProjectConfig_.timestampStart > 0) {
                // if auction configured, look at num tokens in auction
                maxHasBeenInvoked = RAMProjectConfig_.numTokensInAuction == 0;
            } else {
                // if auction not configured, defer to max invocation lib
                maxHasBeenInvoked = MaxInvocationsLib.getMaxHasBeenInvoked({
                    projectId: projectId,
                    coreContract: coreContract
                });
            }
        } else if (projectMinterState == ProjectMinterStates.LiveAuction) {
            // live auction, set to true if num bids >= num tokens in auction
            RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
            maxHasBeenInvoked =
                RAMProjectConfig_.numBids >=
                RAMProjectConfig_.numTokensInAuction;
        } else {
            // post auction, set to true if remaining excess invocations is zero
            (, , uint256 numExcessInvocationsAvailable) = isErrorE1FlagF1({
                projectId: projectId,
                coreContract: coreContract
            });
            maxHasBeenInvoked = numExcessInvocationsAvailable == 0;
        }
    }

    /**
     * Returns balance of project `projectId` on core contract `coreContract`
     * on this minter contract.
     * @param projectId Project ID to get the balance for
     * @param coreContract Core contract address for the given project
     */
    function getProjectBalance(
        uint256 projectId,
        address coreContract
    ) internal view returns (uint256) {
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        return RAMProjectConfig_.projectBalance;
    }

    /**
     * Loads the RAMProjectConfig for a given project and core
     * contract.
     * @param projectId Project Id to get config for
     * @param coreContract Core contract address to get config for
     */
    function getRAMProjectConfig(
        uint256 projectId,
        address coreContract
    ) internal view returns (RAMProjectConfig storage) {
        return s().RAMProjectConfigs[coreContract][projectId];
    }

    /**
     * Loads the RAMAdminMintingConstraint for a given core contract.
     * @param coreContract Core contract address to get config for
     */
    function getRAMAdminMintingConstraintValue(
        address coreContract
    ) internal view returns (AdminMintingConstraint) {
        return s().RAMAdminMintingConstraint[coreContract];
    }

    /**
     * @notice private helper function to mint a token.
     * @dev assumes all checks have been performed
     * @param projectId project ID to mint token for
     * @param coreContract core contract address for the given project
     * @param bidId bid ID of bid to mint token for
     * @param bidder bidder address of bid to mint token for
     * @param minterFilter minter filter contract address
     */
    function _mintTokenForBid(
        uint256 projectId,
        address coreContract,
        uint32 bidId,
        address bidder,
        IMinterFilterV1 minterFilter
    ) private {
        // mint token
        uint256 tokenId = minterFilter.mint_joo({
            to: bidder,
            projectId: projectId,
            coreContract: coreContract,
            sender: msg.sender
        });
        // emit event for state change
        emit BidMinted({
            projectId: projectId,
            coreContract: coreContract,
            bidId: bidId,
            tokenId: tokenId
        });
    }

    /**
     * @notice private helper function to mint and settle bid if not already settled.
     * Assumes check that bidder for bid `bidId` is not null
     * @param projectId Project ID to mint token on.
     * @param coreContract Core contract address for the given project.
     * @param projectPrice Price of a token for the given project.
     * @param slotIndex Slot index of bid.
     * @param bidId ID of bid to settle.
     * @param minterFilter Minter filter contract address
     * @param minterRefundGasLimit Gas limit to use when settling bid, prior to using fallback force-send to refund
     */
    function _mintAndSettle(
        uint256 projectId,
        address coreContract,
        uint256 projectPrice,
        uint256 slotIndex,
        uint256 bidId,
        IMinterFilterV1 minterFilter,
        uint256 minterRefundGasLimit
    ) private {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        Bid storage bid = RAMProjectConfig_.bids[bidId];
        // Mark bid as minted
        _setBidPackedBool({bid: bid, index: INDEX_IS_MINTED, value: true});

        // Mint token for bid
        _mintTokenForBid({
            projectId: projectId,
            coreContract: coreContract,
            bidId: uint32(bidId),
            bidder: bid.bidder,
            minterFilter: minterFilter
        });

        // Settle if not already settled
        // @dev collector could have previously settled bid, so need to
        // settle only if not already settled
        if (!(_getBidPackedBool(bid, INDEX_IS_SETTLED))) {
            _settleBid({
                RAMProjectConfig_: RAMProjectConfig_,
                projectId: projectId,
                coreContract: coreContract,
                projectPrice: projectPrice,
                slotIndex: slotIndex,
                bidId: uint32(bidId),
                minterRefundGasLimit: minterRefundGasLimit
            });
        }
    }

    /**
     * @notice Helper function to handle settling a bid.
     * Reverts if bidder is not the bid's bidder.
     * Reverts if bid has already been settled.
     * @param RAMProjectConfig_ RAMProjectConfig to update
     * @param projectId Project ID of bid to settle
     * @param coreContract Core contract address for the given project.
     * @param projectPrice Price of token on the project
     * @param bidId ID of bid to settle
     * @param bidder Bidder address of bid to settle
     * @param minterRefundGasLimit Gas limit to use when refunding the bidder.
     */
    function _settleBidWithChecks(
        RAMProjectConfig storage RAMProjectConfig_,
        uint256 projectId,
        address coreContract,
        uint256 projectPrice,
        uint32 bidId,
        address bidder,
        uint256 minterRefundGasLimit
    ) private {
        // CHECKS
        Bid storage bid = RAMProjectConfig_.bids[bidId];
        // require bidder is the bid's bidder
        require(bid.bidder == bidder, "Only bidder");
        // require bid is not yet settled
        require(
            !(_getBidPackedBool(bid, INDEX_IS_SETTLED)),
            "Only un-settled bid"
        );

        _settleBid({
            RAMProjectConfig_: RAMProjectConfig_,
            projectId: projectId,
            coreContract: coreContract,
            slotIndex: bid.slotIndex,
            bidId: bidId,
            projectPrice: projectPrice,
            minterRefundGasLimit: minterRefundGasLimit
        });
    }

    /**
     * @notice private helper function to handle settling a bid.
     * @dev assumes bid has not been previously settled, and that all other
     * checks have been performed.
     * @param RAMProjectConfig_ RAMProjectConfig to update
     * @param projectId Project ID of bid to settle
     * @param coreContract Core contract address for the given project.
     * @param slotIndex Slot index of bid to settle
     * @param bidId ID of bid to settle
     * @param projectPrice Price of token on the project
     * @param minterRefundGasLimit Gas limit to use when refunding the previous
     * highest bidder, prior to using fallback force-send to refund
     */
    function _settleBid(
        RAMProjectConfig storage RAMProjectConfig_,
        uint256 projectId,
        address coreContract,
        uint256 slotIndex,
        uint32 bidId,
        uint256 projectPrice,
        uint256 minterRefundGasLimit
    ) private {
        // @dev bid not passed as parameter to avoid stack too deep error in
        // functions that utilize this helper function
        Bid storage bid = RAMProjectConfig_.bids[bidId];
        // EFFECTS
        // update state
        _setBidPackedBool({bid: bid, index: INDEX_IS_SETTLED, value: true});
        // amount due = bid amount - project price
        uint256 amountDue = slotIndexToBidValue({
            basePrice: RAMProjectConfig_.basePrice,
            // @dev safe to cast to uint16
            slotIndex: uint16(slotIndex)
        }) - projectPrice;
        if (amountDue > 0) {
            // force-send settlement to bidder
            // @dev reverts on underflow
            RAMProjectConfig_.projectBalance -= uint120(amountDue);
            SplitFundsLib.forceSafeTransferETH({
                to: bid.bidder,
                amount: amountDue,
                minterRefundGasLimit: minterRefundGasLimit
            });
        }
        // emit event for state change
        emit BidSettled({
            projectId: projectId,
            coreContract: coreContract,
            bidId: bidId
        });
    }

    /**
     * @notice private helper function to settle and refund bids to resolve E1 error state.
     * Assumes check that bidder for bid `bidId` is not null
     * @param projectId Project ID to refund bid for.
     * @param coreContract Core contract address for the given project.
     * @param projectPrice Price of a token for the given project.
     * @param slotIndex Slot index of bid.
     * @param bidId ID of bid to settle.
     * @param minterRefundGasLimit Gas limit to use when refunding bidder
     */
    function _settleAndRefundBid(
        uint256 projectId,
        address coreContract,
        uint256 projectPrice,
        uint256 slotIndex,
        uint256 bidId,
        uint256 minterRefundGasLimit
    ) private {
        // load project config
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        // load bid
        Bid storage bid = RAMProjectConfig_.bids[bidId];
        // @dev bidderAddress previously checked not null
        address bidderAddress = bid.bidder;
        // Settle and refund the Bid
        // Minimum value to send is the project price
        uint256 valueToSend = projectPrice;
        bool didSettleBid = false;

        // if not isSettled, then settle the bid
        if (!_getBidPackedBool(bid, INDEX_IS_SETTLED)) {
            // mark bid as settled
            _setBidPackedBool({bid: bid, index: INDEX_IS_SETTLED, value: true});
            didSettleBid = true;
            // send entire bid value if not previously settled
            valueToSend = slotIndexToBidValue({
                basePrice: RAMProjectConfig_.basePrice,
                slotIndex: uint16(slotIndex)
            });
        }
        // mark bid as refunded
        _setBidPackedBool({bid: bid, index: INDEX_IS_REFUNDED, value: true});
        // INTERACTIONS
        // force-send refund to bidder
        // @dev reverts on underflow
        RAMProjectConfig_.projectBalance -= uint120(valueToSend);
        SplitFundsLib.forceSafeTransferETH({
            to: bidderAddress,
            amount: valueToSend,
            minterRefundGasLimit: minterRefundGasLimit
        });
        // emit event for state changes
        if (didSettleBid) {
            emit BidSettled({
                projectId: projectId,
                coreContract: coreContract,
                bidId: bidId
            });
        }
        emit BidRefunded({
            projectId: projectId,
            coreContract: coreContract,
            bidId: bidId
        });
    }

    /**
     * @notice Helper function to get the price of a token on a project.
     * @dev Assumes project is configured, has a base price, and generally
     * makes sense to get a price for.
     * @param RAMProjectConfig_ RAMProjectConfig to query
     */
    function _getProjectPrice(
        RAMProjectConfig storage RAMProjectConfig_
    ) private view returns (uint256 projectPrice) {
        bool wasSellout = RAMProjectConfig_.numBids >=
            RAMProjectConfig_.numTokensInAuction;
        // price is lowest bid if sellout, otherwise base price
        projectPrice = wasSellout
            ? slotIndexToBidValue({
                basePrice: RAMProjectConfig_.basePrice,
                slotIndex: RAMProjectConfig_.minBidSlotIndex
            })
            : RAMProjectConfig_.basePrice;
    }

    /**
     * @notice Helper function to get the number of tokens owed for a given
     * project.
     * @param RAMProjectConfig_ RAMProjectConfig to query
     * @return tokensOwed The number of bids in a project minus the sum of tokens already
     * minted and bids that have been refunded due to an error state.
     */
    function _getNumTokensOwed(
        RAMProjectConfig storage RAMProjectConfig_
    ) private view returns (uint256 tokensOwed) {
        tokensOwed =
            RAMProjectConfig_.numBids -
            (RAMProjectConfig_.numBidsMintedTokens +
                RAMProjectConfig_.numBidsErrorRefunded);
    }

    /**
     * @notice Inserts a bid into the project's RAMProjectConfig.
     * Assumes the bid is valid and may be inserted into the bucket-sort data
     * structure.
     * Creates a new bid if bidId is zero, otherwise moves an existing bid,
     * which is assumed to exist and be valid.
     * Emits BidCreated event if a new bid is created.
     * @dev assumes slot index is valid and < NUM_SLOTS
     * @param RAMProjectConfig_ RAM project config to insert bid into
     * @param projectId Project ID to insert bid for
     * @param coreContract Core contract address to insert bid for
     * @param slotIndex Slot index to insert bid at
     * @param bidder Bidder address
     * @param bidId Bid ID to insert, or zero if a new bid should be created
     */
    function _insertBid(
        RAMProjectConfig storage RAMProjectConfig_,
        uint256 projectId,
        address coreContract,
        uint16 slotIndex,
        address bidder,
        uint32 bidId
    ) private {
        // add the new Bid to tail of the slot's doubly linked list
        bool createNewBid = bidId == 0;
        if (createNewBid) {
            // prefix ++ to skip initial bid ID of zero (indicates null value)
            bidId = ++RAMProjectConfig_.nextBidId;
        }
        uint256 prevTailBidId = RAMProjectConfig_.tailBidIdBySlot[slotIndex];
        RAMProjectConfig_.bids[bidId] = Bid({
            prevBidId: uint32(prevTailBidId),
            nextBidId: 0, // null value at end of tail
            slotIndex: slotIndex,
            bidder: bidder,
            packedBools: 0 // all packed bools false
        });
        // update tail pointer to new bid
        RAMProjectConfig_.tailBidIdBySlot[slotIndex] = bidId;
        // update head pointer or next pointer of previous bid
        if (prevTailBidId == 0) {
            // first bid in slot, update head pointer
            RAMProjectConfig_.headBidIdBySlot[slotIndex] = bidId;
        } else {
            // update previous bid's next pointer
            RAMProjectConfig_.bids[prevTailBidId].nextBidId = bidId;
        }

        // update number of active bids
        RAMProjectConfig_.numBids++;
        // update metadata if first bid for this slot
        // @dev assumes minting has not yet started
        if (prevTailBidId == 0) {
            // set the slot in the bitmap
            _setBitmapSlot({
                RAMProjectConfig_: RAMProjectConfig_,
                slotIndex: slotIndex
            });
            // update bitmap metadata - reduce min bid index if necessary
            if (slotIndex < RAMProjectConfig_.minBidSlotIndex) {
                RAMProjectConfig_.minBidSlotIndex = slotIndex;
            }
            // update bitmap metadata - increase max bid index if necessary
            if (slotIndex > RAMProjectConfig_.maxBidSlotIndex) {
                RAMProjectConfig_.maxBidSlotIndex = slotIndex;
            }
        }

        if (createNewBid) {
            // emit state change event
            emit BidCreated({
                projectId: projectId,
                coreContract: coreContract,
                slotIndex: slotIndex,
                bidId: bidId,
                bidder: bidder
            });
        }
    }

    /**
     * @notice Remove minimum bid from the project's RAMProjectConfig.
     * Reverts if no bids exist in slot RAMProjectConfig_.minBidSlotIndex.
     * @param RAMProjectConfig_ RAM project config to remove bid from
     * @param projectId Project ID to remove bid from
     * @param coreContract Core contract address for the given project
     * @param minterRefundGasLimit Gas limit to use when refunding the previous
     * highest bidder, prior to using fallback force-send to refund
     * @return removedBidAmount The value of the removed bid, in Wei
     */
    function _removeMinBid(
        RAMProjectConfig storage RAMProjectConfig_,
        uint256 projectId,
        address coreContract,
        uint256 minterRefundGasLimit
    ) private returns (uint256 removedBidAmount) {
        // get the minimum bid slot and bid id
        uint16 removedSlotIndex = RAMProjectConfig_.minBidSlotIndex;
        uint256 removedBidId = RAMProjectConfig_.tailBidIdBySlot[
            removedSlotIndex
        ];
        // @dev no coverage on else branch because it is unreachable as used
        require(removedBidId > 0, "No bids");
        // record the previous min bidder
        Bid storage removedBid = RAMProjectConfig_.bids[removedBidId];
        address removedBidder = removedBid.bidder;
        // update the tail pointer of the slot's doubly linked list
        uint32 newTailBidId = removedBid.prevBidId;
        RAMProjectConfig_.tailBidIdBySlot[removedSlotIndex] = newTailBidId;

        RAMProjectConfig_.numBids--;
        // update metadata if no more active bids for this slot
        if (newTailBidId == 0) {
            // update the head pointer of the slot's doubly linked list
            RAMProjectConfig_.headBidIdBySlot[removedSlotIndex] = 0;

            // unset the slot in the bitmap
            // update minBidIndex, efficiently starting at minBidSlotIndex + 1
            _unsetBitmapSlot({
                RAMProjectConfig_: RAMProjectConfig_,
                slotIndex: removedSlotIndex
            });
            // @dev reverts if removedSlotIndex was the maximum slot 511,
            // preventing bids from being removed entirely from the last slot,
            // which is acceptable and non-impacting for this minter
            // @dev sets minBidSlotIndex to 512 if no more active bids, which
            // is desired behavior for this minter
            RAMProjectConfig_.minBidSlotIndex = _getMinSlotWithBid({
                RAMProjectConfig_: RAMProjectConfig_,
                startSlotIndex: removedSlotIndex + 1
            });
        } else {
            // if the removed bid was not the head, then unset the nextBidId pointer of the bid
            Bid storage newTailBid = RAMProjectConfig_.bids[newTailBidId];
            newTailBid.nextBidId = 0;
        }
        // refund the removed bidder
        removedBidAmount = slotIndexToBidValue({
            basePrice: RAMProjectConfig_.basePrice,
            slotIndex: removedSlotIndex
        });
        // @dev reverts on underflow
        RAMProjectConfig_.projectBalance -= uint120(removedBidAmount);

        // delete the removed bid to prevent future claiming
        // @dev performed last to avoid pointing to deleted bid struct
        delete RAMProjectConfig_.bids[removedBidId];

        SplitFundsLib.forceSafeTransferETH({
            to: removedBidder,
            amount: removedBidAmount,
            minterRefundGasLimit: minterRefundGasLimit
        });
        // emit state change event
        emit BidRemoved({
            projectId: projectId,
            coreContract: coreContract,
            bidId: removedBidId
        });
    }

    /**
     * @notice Ejects a bid from the project's RAMProjectConfig.
     * Assumes the bid is valid (i.e. bid ID is a valid, active bid).
     * Does not refund the bidder, does not emit events, does not delete Bid.
     * @param RAMProjectConfig_ RAM project config to eject bid from
     * @param slotIndex Slot index to eject bid from
     * @param bidId ID of bid to eject
     */
    function _ejectBidFromSlot(
        RAMProjectConfig storage RAMProjectConfig_,
        uint16 slotIndex,
        uint256 bidId
    ) private {
        // get the bid to remove
        Bid storage removedBid = RAMProjectConfig_.bids[bidId];
        uint32 prevBidId = removedBid.prevBidId;
        uint32 nextBidId = removedBid.nextBidId;
        // update previous bid's next pointer
        if (prevBidId == 0) {
            // removed bid was the head bid
            RAMProjectConfig_.headBidIdBySlot[slotIndex] = nextBidId;
        } else {
            // removed bid was not the head bid
            RAMProjectConfig_.bids[prevBidId].nextBidId = nextBidId;
        }
        // update next bid's previous pointer
        if (nextBidId == 0) {
            // removed bid was the tail bid
            RAMProjectConfig_.tailBidIdBySlot[slotIndex] = prevBidId;
        } else {
            // removed bid was not the tail bid
            RAMProjectConfig_.bids[nextBidId].prevBidId = prevBidId;
        }

        // decrement the number of active bids
        RAMProjectConfig_.numBids--;

        // update metadata if no more active bids for this slot
        if (prevBidId == 0 && nextBidId == 0) {
            // unset the slot in the bitmap
            // update minBidIndex, efficiently starting at minBidSlotIndex + 1
            _unsetBitmapSlot({
                RAMProjectConfig_: RAMProjectConfig_,
                slotIndex: slotIndex
            });
            // @dev reverts if removedSlotIndex was the maximum slot 511,
            // preventing bids from being removed entirely from the last slot,
            // which is acceptable and non-impacting for this minter
            // @dev sets minBidSlotIndex to 512 if no more active bids, which
            // is desired behavior for this minter
            if (RAMProjectConfig_.minBidSlotIndex == slotIndex) {
                RAMProjectConfig_.minBidSlotIndex = _getMinSlotWithBid({
                    RAMProjectConfig_: RAMProjectConfig_,
                    startSlotIndex: slotIndex + 1
                });
            }
        }

        // @dev do not refund, do not emit event, do not delete bid
    }

    /**
     * @notice Helper function to handle setting slot in 512-bit bitmap
     * Reverts if slotIndex > 511
     * @param slotIndex Index of slot to set (between 0 and 511)
     * @param RAMProjectConfig_ RAMProjectConfig to update
     */
    function _setBitmapSlot(
        RAMProjectConfig storage RAMProjectConfig_,
        uint256 slotIndex
    ) private {
        // revert if slotIndex >= NUM_SLOTS, since this is an invalid input
        // @dev no coverage as slot index out of range checked in placeBid and implicitly in topUpBid
        require(slotIndex < NUM_SLOTS, "Only slot index lt NUM_SLOTS");
        // set the slot in the bitmap
        if (slotIndex < 256) {
            // @dev <256 conditional ensures no overflow when casting to uint8
            RAMProjectConfig_.slotsBitmapA = RAMProjectConfig_.slotsBitmapA.set(
                uint8(slotIndex)
            );
        } else {
            // @dev <512 results in no overflow when casting to uint8
            RAMProjectConfig_.slotsBitmapB = RAMProjectConfig_.slotsBitmapB.set(
                // @dev casting to uint8 intentional overflow instead of
                // subtracting 256 from slotIndex
                uint8(slotIndex)
            );
        }
    }

    /**
     * @notice Helper function to handle unsetting slot in 512-bit bitmap
     * Reverts if slotIndex > 511
     * @param slotIndex Index of slot to set (between 0 and 511)
     * @param RAMProjectConfig_ RAMProjectConfig to update
     */
    function _unsetBitmapSlot(
        RAMProjectConfig storage RAMProjectConfig_,
        uint256 slotIndex
    ) private {
        // revert if slotIndex >= NUM_SLOTS, since this is an invalid input
        // @dev no coverage as slot index out of range checked in placeBid and implicitly in topUpBid
        require(slotIndex < NUM_SLOTS, "Only slot index lt NUM_SLOTS");
        // unset the slot in the bitmap
        if (slotIndex < 256) {
            // @dev <256 conditional ensures no overflow when casting to uint8
            RAMProjectConfig_.slotsBitmapA = RAMProjectConfig_
                .slotsBitmapA
                .unset(uint8(slotIndex));
        } else {
            // @dev <512 results in no overflow when casting to uint8
            RAMProjectConfig_.slotsBitmapB = RAMProjectConfig_
                .slotsBitmapB
                .unset(
                    // @dev casting to uint8 intentional overflow instead of
                    // subtracting 256 from slotIndex
                    uint8(slotIndex)
                );
        }
    }

    /**
     * @notice Helper function to set a packed boolean in a Bid struct.
     * @param bid Bid to update
     * @param index Index of packed boolean to update
     * @param value Value to set packed boolean to
     */
    function _setBidPackedBool(
        Bid storage bid,
        uint8 index,
        bool value
    ) private {
        // @dev no coverage on else branch because it is unreachable as used
        if (value) {
            bid.packedBools = uint8(
                uint256(bid.packedBools).setBoolTrue(index)
            );
        } else {
            bid.packedBools = uint8(
                uint256(bid.packedBools).setBoolFalse(index)
            );
        }
    }

    /**
     * @notice Helper function to get a packed boolean from a Bid struct.
     * @param bid Bid to query
     * @param index Index of packed boolean to query
     * @return Value of packed boolean
     */
    function _getBidPackedBool(
        Bid storage bid,
        uint8 index
    ) private view returns (bool) {
        return uint256(bid.packedBools).getBool(index);
    }

    /**
     * @notice Helper function to get minimum slot index with an active bid,
     * starting at a given slot index and searching upwards.
     * Returns 512, (invalid slot index) if no slots with bids were found.
     * Reverts if startSlotIndex > 511, since this library only supports 512
     * slots.
     * @param RAMProjectConfig_ RAM project config to query
     * @param startSlotIndex Slot index to start search at
     * @return minSlotWithBid Minimum slot index with an active bid, or 512 (invalid index) if
     * no slots with bids were found.
     */
    function _getMinSlotWithBid(
        RAMProjectConfig storage RAMProjectConfig_,
        uint16 startSlotIndex
    ) private view returns (uint16 minSlotWithBid) {
        bool foundSlotWithBid;
        // revert if startSlotIndex > 511, since this is an invalid input
        // @dev no coverage on if branch because unreachable as used
        if (startSlotIndex > 511) {
            revert("Only start slot index lt 512");
        }
        // temporary uint256 in working memory
        uint256 minSlotWithBid_;
        // start at startSlotIndex
        if (startSlotIndex > 255) {
            // @dev <512 check results in no overflow when casting to uint8
            (minSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
                .slotsBitmapB
                .minBitSet(
                    // @dev casting to uint8 intentional overflow instead of
                    // subtracting 256 from slotIndex
                    uint8(startSlotIndex)
                );
            // add 256 to account for slotsBitmapB offset
            minSlotWithBid_ += 256;
        } else {
            // @dev <256 conditional ensures no overflow when casting to uint8
            (minSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
                .slotsBitmapA
                .minBitSet(uint8(startSlotIndex));

            // if no bids in first bitmap, check second bitmap
            // @dev behavior of library's minBitSet is to return 256 if no bits
            // were set
            if (!foundSlotWithBid) {
                // @dev <512 check results in no overflow when casting to uint8
                (minSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
                    .slotsBitmapB
                    .minBitSet(
                        // start at beginning of second bitmap
                        uint8(0)
                    );
                // add 256 to account for slotsBitmapB offset
                minSlotWithBid_ += 256;
            }
        }
        // populate return value
        if (!foundSlotWithBid) {
            return uint16(NUM_SLOTS);
        } else {
            minSlotWithBid = uint16(minSlotWithBid_);
            return minSlotWithBid;
        }
    }

    /**
     * @notice Helper function to get maximum slot index with an active bid,
     * starting at a given slot index and searching downwards.
     * Returns 512, (invalid slot index) if no slots with bids were found.
     * Reverts if startSlotIndex > 511, since this library only supports 512
     * slots.
     * @param RAMProjectConfig_ RAM project config to query
     * @param startSlotIndex Slot index to start search at
     * @return maxSlotWithBid Maximum slot index with an active bid, and 512 (invalid index) if
     * no slots with bids were found.
     */
    function _getMaxSlotWithBid(
        RAMProjectConfig storage RAMProjectConfig_,
        uint16 startSlotIndex
    ) private view returns (uint16 maxSlotWithBid) {
        bool foundSlotWithBid;
        // revert if startSlotIndex > 511, since this is an invalid input
        // @dev no coverage on if branch because unreachable as used
        if (startSlotIndex > 511) {
            revert("Only start slot index lt 512");
        }
        // temporary uint256 in working memory
        uint256 maxSlotWithBid_;
        // start at startSlotIndex
        if (startSlotIndex < 256) {
            // @dev <256 conditional ensures no overflow when casting to uint8
            (maxSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
                .slotsBitmapA
                .maxBitSet(uint8(startSlotIndex));
        } else {
            // need to potentially check both bitmaps
            (maxSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
                .slotsBitmapB
                .maxBitSet(
                    // @dev casting to uint8 intentional overflow instead of
                    // subtracting 256 from slotIndex
                    uint8(startSlotIndex)
                );
            // add 256 to account for slotsBitmapB offset
            maxSlotWithBid_ += 256;
            if (!foundSlotWithBid) {
                // no bids in first bitmap B, so check second bitmap A
                (maxSlotWithBid_, foundSlotWithBid) = RAMProjectConfig_
                    .slotsBitmapA
                    .maxBitSet(
                        // start at the end of the first bitmap
                        uint8(255)
                    );
            }
        }
        // populate return value
        // @dev no coverage on if branch because it is unreachable as used
        if (!foundSlotWithBid) {
            return uint16(NUM_SLOTS);
        } else {
            maxSlotWithBid = uint16(maxSlotWithBid_);
            return maxSlotWithBid;
        }
    }

    /**
     * @notice Returns the next valid bid slot index and value for a given project.
     * @dev this may return slot index and value higher than the maximum slot index and value
     * allowed by the minter, in which case a bid cannot actually be placed
     * to outbid a bid at `startSlotIndex`.
     * @param projectId Project ID to find next valid bid slot index for
     * @param coreContract Core contract address for the given project
     * @param startSlotIndex Slot index to start search from
     * @return nextValidBidSlotIndex Next valid bid slot index
     * @return nextValidBidValue Next valid bid value at nextValidBidSlotIndex slot index, in Wei
     */
    function _findNextValidBidSlotIndexAndValue(
        uint256 projectId,
        address coreContract,
        uint16 startSlotIndex
    )
        private
        view
        returns (uint16 nextValidBidSlotIndex, uint256 nextValidBidValue)
    {
        RAMProjectConfig storage RAMProjectConfig_ = getRAMProjectConfig({
            projectId: projectId,
            coreContract: coreContract
        });
        uint88 basePrice = RAMProjectConfig_.basePrice;
        uint256 startBidValue = slotIndexToBidValue({
            basePrice: basePrice,
            slotIndex: startSlotIndex
        });
        // start search at next slot, incremented in while loop
        uint16 currentSlotIndex = startSlotIndex;
        while (true) {
            // increment slot index and re-calc current slot bid value
            unchecked {
                currentSlotIndex++;
            }
            nextValidBidValue = slotIndexToBidValue({
                basePrice: basePrice,
                slotIndex: currentSlotIndex
            });
            // break if current slot's bid value is sufficiently greater than
            // the starting slot's bid value
            if (
                _isSufficientOutbid({
                    oldBidValue: startBidValue,
                    newBidValue: nextValidBidValue
                })
            ) {
                break;
            }
            // otherwise continue to next iteration
        }
        // return the found valid slot index
        nextValidBidSlotIndex = currentSlotIndex;
    }

    /**
     * @notice Returns a bool indicating if a new bid value is sufficiently
     * greater than an old bid value, to replace the old bid value.
     * @param oldBidValue Old bid value to compare
     * @param newBidValue New bid value to compare
     * @return isSufficientOutbid True if new bid is sufficiently greater than
     * old bid, false otherwise
     */
    function _isSufficientOutbid(
        uint256 oldBidValue,
        uint256 newBidValue
    ) private pure returns (bool) {
        if (oldBidValue > 0.5 ether) {
            // require new bid is at least 2.5% greater than removed minimum bid
            return newBidValue > (oldBidValue * 10250) / 10000;
        }
        // require new bid is at least 5% greater than removed minimum bid
        return newBidValue > (oldBidValue * 10500) / 10000;
    }

    /**
     * @notice Returns the value of a bid in a given slot, in Wei.
     * @dev returns 0 if base price is zero
     * @param basePrice Base price (or reserve price) of the auction, in Wei
     * @param slotIndex Slot index to query
     * @return slotBidValue Value of a bid in the slot, in Wei
     */
    function slotIndexToBidValue(
        uint88 basePrice,
        uint16 slotIndex
    ) internal pure returns (uint256 slotBidValue) {
        // @dev for overflow safety, always revert if slotIndex >= NUM_SLOTS
        require(slotIndex < NUM_SLOTS, "Only slot index lt NUM_SLOTS");
        // use pseud-exponential pricing curve
        // multiply by two (via bit-shifting) for the number of entire
        // slots-per-price-double associated with the slot index
        // @dev overflow not possible due to typing, constants, and check above
        // (max(uint88) << (512 / 64)) < max(uint256)
        slotBidValue =
            uint256(basePrice) <<
            (slotIndex / SLOTS_PER_PRICE_DOUBLE);
        // perform a linear interpolation between partial half-life points, to
        // approximate the current place on a perfect exponential curve.
        // @dev overflow automatically checked in solidity 0.8, not expected
        slotBidValue +=
            (slotBidValue * (slotIndex % SLOTS_PER_PRICE_DOUBLE)) /
            SLOTS_PER_PRICE_DOUBLE;
    }

    /**
     * @notice Return the storage struct for reading and writing. This library
     * uses a diamond storage pattern when managing storage.
     * @return storageStruct The RAMLibStorage struct.
     */
    function s() private pure returns (RAMLibStorage storage storageStruct) {
        bytes32 position = RAM_LIB_STORAGE_POSITION;
        assembly ("memory-safe") {
            storageStruct.slot := position
        }
    }
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

import {IMinterBaseV0} from "../../../interfaces/v0.8.x/IMinterBaseV0.sol";
import {IGenArt721CoreContractV3_Base} from "../../../interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol";
import {IGenArt721CoreContractV3} from "../../../interfaces/v0.8.x/IGenArt721CoreContractV3.sol";
import {IGenArt721CoreContractV3_Engine} from "../../../interfaces/v0.8.x/IGenArt721CoreContractV3_Engine.sol";

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

/**
 * @title Art Blocks Split Funds Library
 * @notice This library is designed for the Art Blocks platform. It splits
 * Ether (ETH) and ERC20 token funds among stakeholders, such as sender
 * (if refund is applicable), providers, artists, and artists' additional
 * payees.
 * @author Art Blocks Inc.
 */

library SplitFundsLib {
    /**
     * @notice Currency updated for project `projectId` to symbol
     * `currencySymbol` and address `currencyAddress`.
     * @param projectId Project ID currency was updated for
     * @param coreContract Core contract address currency was updated for
     * @param currencyAddress Currency address
     * @param currencySymbol Currency symbol
     */
    event ProjectCurrencyInfoUpdated(
        uint256 indexed projectId,
        address indexed coreContract,
        address indexed currencyAddress,
        string currencySymbol
    );

    // position of Split Funds Lib storage, using a diamond storage pattern
    // for this library
    bytes32 constant SPLIT_FUNDS_LIB_STORAGE_POSITION =
        keccak256("splitfundslib.storage");

    // contract-level variables
    struct IsEngineCache {
        bool isEngine;
        bool isCached;
    }

    // project-level variables
    struct SplitFundsProjectConfig {
        address currencyAddress; // address(0) if ETH
        string currencySymbol; // Assumed to be ETH if null
    }

    // Diamond storage pattern is used in this library
    struct SplitFundsLibStorage {
        mapping(address coreContract => mapping(uint256 projectId => SplitFundsProjectConfig)) splitFundsProjectConfigs;
        mapping(address coreContract => IsEngineCache) isEngineCacheConfigs;
    }

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

    /**
     * @notice pays ETH funds to sender (if refund), with all of token price
     * being sent to the render provider for a token purchased on project
     * `projectId`.
     * WARNING: This function uses msg.value and msg.sender to determine
     * refund amounts, and therefore may not be applicable to all use cases
     * (e.g. do not use with Dutch Auctions with on-chain settlement).
     * @dev This function relies on msg.sender and msg.value, so it must be
     * called directly from the contract that is receiving the payment.
     * @dev possible DoS during splits is acknowledged, and mitigated by
     * business practices, including end-to-end testing on mainnet, and
     * admin-accepted artist payment addresses.
     * @param projectId Project ID for which funds shall be split.
     * @param pricePerTokenInWei Current price of token, in Wei.
     * @param coreContract Address of the GenArt721CoreContract associated
     * with the project.
     */
    function sendAllToRenderProviderETHRefundSender(
        uint256 projectId,
        uint256 pricePerTokenInWei,
        address coreContract
    ) internal {
        if (msg.value > 0) {
            // send refund to sender
            uint256 refund = msg.value - pricePerTokenInWei;
            if (refund > 0) {
                (bool success_, ) = msg.sender.call{value: refund}("");
                require(success_, "Refund failed");
            }
            // send remaining to render provider
            sendAllToRenderProviderETHNoRefund({
                projectId: projectId,
                valueInWei: pricePerTokenInWei,
                coreContract: coreContract
            });
        }
    }

    /**
     * @notice Splits ETH revenues between providers, artist, and artist's
     * additional payee for revenue generated by project `projectId`.
     * This function does NOT refund msg.sender, and does NOT use msg.value
     * when determining the value to be split.
     * @dev possible DoS during splits is acknowledged, and mitigated by
     * business practices, including end-to-end testing on mainnet, and
     * admin-accepted artist payment addresses.
     * @param projectId Project ID for which funds shall be split.
     * @param valueInWei Value to be split, in Wei.
     * @param coreContract Address of the GenArt721CoreContract
     * associated with the project.
     */
    function splitRevenuesETHNoRefund(
        uint256 projectId,
        uint256 valueInWei,
        address coreContract
    ) internal {
        if (valueInWei == 0) {
            return; // return early
        }
        // split funds between platforms, artist, and artist's
        // additional payee
        bool isEngine_ = isEngine(coreContract);
        uint256 renderProviderRevenue;
        address payable renderProviderAddress;
        uint256 platformProviderRevenue;
        address payable platformProviderAddress;
        uint256 artistRevenue;
        address payable artistAddress;
        uint256 additionalPayeePrimaryRevenue;
        address payable additionalPayeePrimaryAddress;
        if (isEngine_) {
            // get engine splits
            (
                renderProviderRevenue,
                renderProviderAddress,
                platformProviderRevenue,
                platformProviderAddress,
                artistRevenue,
                artistAddress,
                additionalPayeePrimaryRevenue,
                additionalPayeePrimaryAddress
            ) = IGenArt721CoreContractV3_Engine(coreContract)
                .getPrimaryRevenueSplits({
                    _projectId: projectId,
                    _price: valueInWei
                });
        } else {
            // get flagship splits
            // @dev note that platformProviderAddress and
            // platformProviderRevenue remain 0 for flagship
            (
                renderProviderRevenue, // artblocks revenue
                renderProviderAddress, // artblocks address
                artistRevenue,
                artistAddress,
                additionalPayeePrimaryRevenue,
                additionalPayeePrimaryAddress
            ) = IGenArt721CoreContractV3(coreContract).getPrimaryRevenueSplits({
                _projectId: projectId,
                _price: valueInWei
            });
        }
        // require total revenue split is 100%
        // @dev note that platformProviderRevenue remains 0 for flagship
        require(
            renderProviderRevenue +
                platformProviderRevenue +
                artistRevenue +
                additionalPayeePrimaryRevenue ==
                valueInWei,
            "Invalid revenue split totals"
        );
        // distribute revenues
        // @dev note that platformProviderAddress and platformProviderRevenue
        // remain 0 for flagship
        _sendPaymentsETH({
            platformProviderRevenue: platformProviderRevenue,
            platformProviderAddress: platformProviderAddress,
            renderProviderRevenue: renderProviderRevenue,
            renderProviderAddress: renderProviderAddress,
            artistRevenue: artistRevenue,
            artistAddress: artistAddress,
            additionalPayeePrimaryRevenue: additionalPayeePrimaryRevenue,
            additionalPayeePrimaryAddress: additionalPayeePrimaryAddress
        });
    }

    /**
     * @notice Sends all revenue generated by project `projectId` to render
     * provider.
     * This function does NOT refund msg.sender, and does NOT use msg.value
     * when determining the value to be split.
     * @dev possible DoS during splits is acknowledged, and mitigated by
     * business practices, including end-to-end testing on mainnet, and
     * admin-accepted payment addresses.
     * @param projectId Project ID for which funds shall be sent.
     * @param valueInWei Value to be sent, in Wei.
     * @param coreContract Address of the GenArt721CoreContract
     * associated with the project.
     */
    function sendAllToRenderProviderETHNoRefund(
        uint256 projectId,
        uint256 valueInWei,
        address coreContract
    ) internal {
        if (valueInWei == 0) {
            return; // return early
        }
        // split funds between platforms, artist, and artist's
        // additional payee
        bool isEngine_ = isEngine(coreContract);
        address payable renderProviderAddress;
        if (isEngine_) {
            // get engine splits
            (
                ,
                renderProviderAddress,
                ,
                ,
                ,
                ,
                ,

            ) = IGenArt721CoreContractV3_Engine(coreContract)
                .getPrimaryRevenueSplits({
                    _projectId: projectId,
                    _price: valueInWei
                });
        } else {
            // get flagship splits
            // @dev note that platformProviderAddress and
            // platformProviderRevenue remain 0 for flagship
            (
                ,
                // artblocks revenue
                renderProviderAddress, // artblocks address
                ,
                ,
                ,

            ) = IGenArt721CoreContractV3(coreContract).getPrimaryRevenueSplits({
                _projectId: projectId,
                _price: valueInWei
            });
        }
        require(
            renderProviderAddress != address(0),
            "Render Provider address not set"
        );
        // distribute revenue
        // Render Provider / Art Blocks payment
        // @dev previous conditional ensures valueInWei is non-zero
        (bool success, ) = renderProviderAddress.call{value: valueInWei}("");
        require(success, "Render Provider payment failed");
    }

    /**
     * @notice Splits ERC20 funds between providers, artist, and artist's
     * additional payee, for a token purchased on project `projectId`.
     * The function performs checks to ensure that the ERC20 token is
     * approved for transfer, and that a non-zero ERC20 token address is
     * configured.
     * @dev This function relies on msg.sender, so it must be
     * called directly from the contract that is receiving the payment.
     * @dev possible DoS during splits is acknowledged, and mitigated by
     * business practices, including end-to-end testing on mainnet, and
     * admin-accepted artist payment addresses.
     * @param projectId Project ID for which funds shall be split.
     * @param pricePerToken Current price of token, in base units. For example,
     * if the ERC20 token has 6 decimals, an input value of `1_000_000` would
     * represent a price of `1.000000` tokens.
     * @param coreContract Core contract address.
     */
    function splitFundsERC20(
        uint256 projectId,
        uint256 pricePerToken,
        address coreContract
    ) internal {
        if (pricePerToken == 0) {
            return; // nothing to split, return early
        }
        IERC20 projectCurrency;
        // block scope to avoid stack too deep error
        {
            SplitFundsProjectConfig
                storage splitFundsProjectConfig = getSplitFundsProjectConfig({
                    projectId: projectId,
                    coreContract: coreContract
                });
            address currencyAddress = splitFundsProjectConfig.currencyAddress;
            require(
                currencyAddress != address(0),
                "ERC20: payment not configured"
            );
            // ERC20 token is used for payment
            validateERC20Approvals({
                msgSender: msg.sender,
                currencyAddress: currencyAddress,
                pricePerToken: pricePerToken
            });
            projectCurrency = IERC20(currencyAddress);
        }
        // split remaining funds between foundation, artist, and artist's additional payee
        bool isEngine_ = isEngine(coreContract);
        uint256 renderProviderRevenue;
        address payable renderProviderAddress;
        uint256 platformProviderRevenue;
        address payable platformProviderAddress;
        uint256 artistRevenue;
        address payable artistAddress;
        uint256 additionalPayeePrimaryRevenue;
        address payable additionalPayeePrimaryAddress;
        if (isEngine_) {
            // get engine splits
            (
                renderProviderRevenue,
                renderProviderAddress,
                platformProviderRevenue,
                platformProviderAddress,
                artistRevenue,
                artistAddress,
                additionalPayeePrimaryRevenue,
                additionalPayeePrimaryAddress
            ) = IGenArt721CoreContractV3_Engine(coreContract)
                .getPrimaryRevenueSplits({
                    _projectId: projectId,
                    _price: pricePerToken
                });
        } else {
            // get flagship splits
            // @dev note that platformProviderAddress and
            // platformProviderRevenue remain 0 for flagship
            (
                renderProviderRevenue, // artblocks revenue
                renderProviderAddress, // artblocks address
                artistRevenue,
                artistAddress,
                additionalPayeePrimaryRevenue,
                additionalPayeePrimaryAddress
            ) = IGenArt721CoreContractV3(coreContract).getPrimaryRevenueSplits({
                _projectId: projectId,
                _price: pricePerToken
            });
        }
        // require total revenue split is 100%
        // @dev note that platformProviderRevenue remains 0 for flagship
        require(
            renderProviderRevenue +
                platformProviderRevenue +
                artistRevenue +
                additionalPayeePrimaryRevenue ==
                pricePerToken,
            "Invalid revenue split totals"
        );
        // distribute revenues
        // @dev note that platformProviderAddress and platformProviderRevenue
        // remain 0 for flagship
        _sendPaymentsERC20({
            projectCurrency: projectCurrency,
            platformProviderRevenue: platformProviderRevenue,
            platformProviderAddress: platformProviderAddress,
            renderProviderRevenue: renderProviderRevenue,
            renderProviderAddress: renderProviderAddress,
            artistRevenue: artistRevenue,
            artistAddress: artistAddress,
            additionalPayeePrimaryRevenue: additionalPayeePrimaryRevenue,
            additionalPayeePrimaryAddress: additionalPayeePrimaryAddress
        });
    }

    /**
     * @notice Updates payment currency of the referenced
     * SplitFundsProjectConfig to be `currencySymbol` at address
     * `currencyAddress`.
     * Only supports setting currency info of ERC20 tokens.
     * Returns bool that is true if the price should be reset after this
     * update. Price is recommended to be reset if the currency address was
     * previously configured, but is now being updated to a different currency
     * address. This is to protect accidental price reductions when changing
     * currency if an artist is changing currencies in an unpaused state.
     * @dev artist-defined currency symbol is used instead of any on-chain
     * currency symbol.
     * @param projectId Project ID to update.
     * @param coreContract Core contract address.
     * @param currencySymbol Currency symbol.
     * @param currencyAddress Currency address.
     * @return recommendPriceReset True if the price should be reset after this
     * update.
     */
    function updateProjectCurrencyInfoERC20(
        uint256 projectId,
        address coreContract,
        string memory currencySymbol,
        address currencyAddress
    ) internal returns (bool recommendPriceReset) {
        // CHECKS
        require(currencyAddress != address(0), "null address, only ERC20");
        require(bytes(currencySymbol).length > 0, "only non-null symbol");
        // EFFECTS
        SplitFundsProjectConfig
            storage splitFundsProjectConfig = getSplitFundsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        // recommend price reset if currency address was previously configured
        recommendPriceReset = (splitFundsProjectConfig.currencyAddress !=
            address(0));
        splitFundsProjectConfig.currencySymbol = currencySymbol;
        splitFundsProjectConfig.currencyAddress = currencyAddress;

        emit ProjectCurrencyInfoUpdated({
            projectId: projectId,
            coreContract: coreContract,
            currencyAddress: currencyAddress,
            currencySymbol: currencySymbol
        });
    }

    /**
     * @notice Force sends `amount` (in wei) ETH to `to`, with a gas stipend
     * equal to `minterRefundGasLimit`.
     * If sending via the normal procedure fails, force sends the ETH by
     * creating a temporary contract which uses `SELFDESTRUCT` to force send
     * the ETH.
     * Reverts if the current contract has insufficient balance.
     * @param to The address to send ETH to.
     * @param amount The amount of ETH to send.
     * @param minterRefundGasLimit The gas limit to use when sending ETH, prior
     * to fallback.
     * @dev This function is adapted from the `forceSafeTransferETH` function
     * in the `https://github.com/Vectorized/solady` repository, with
     * modifications to not check if the current contract has sufficient
     * balance. Therefore, the contract should be checked for sufficient
     * balance before calling this function in the minter itself, if
     * applicable.
     */
    function forceSafeTransferETH(
        address to,
        uint256 amount,
        uint256 minterRefundGasLimit
    ) internal {
        // Manually inlined because the compiler doesn't inline functions with
        // branches.
        /// @solidity memory-safe-assembly
        assembly {
            // @dev intentionally do not check if this contract has sufficient
            // balance, because that is not intended to be a valid state.

            // Transfer the ETH and check if it succeeded or not.
            if iszero(call(minterRefundGasLimit, to, amount, 0, 0, 0, 0)) {
                // if the transfer failed, we create a temporary contract with
                // initialization code that uses `SELFDESTRUCT` to force send
                // the ETH.
                // note: Compatible with `SENDALL`:
                // https://eips.ethereum.org/EIPS/eip-4758

                //---------------------------------------------------------------------------------------------------------------//
                // Opcode  | Opcode + Arguments  | Description        | Stack View                                               //
                //---------------------------------------------------------------------------------------------------------------//
                // Contract creation code that uses `SELFDESTRUCT` to force send ETH to a specified address.                     //
                // Creation code summary: 0x73<20-byte toAddress>0xff                                                            //
                //---------------------------------------------------------------------------------------------------------------//
                // 0x73    |  0x73_toAddress     | PUSH20 toAddress   | toAddress                                                //
                // 0xFF    |  0xFF               | SELFDESTRUCT       |                                                          //
                //---------------------------------------------------------------------------------------------------------------//
                // Store the address in scratch space, starting at 0x00, which begins the 20-byte address at 32-20=12 in memory
                // @dev use scratch space because we have enough space for simple creation code (less than 0x40 bytes)
                mstore(0x00, to)
                // store opcode PUSH20 immediately before the address, starting at 0x0b (11) in memory
                mstore8(0x0b, 0x73)
                // store opcode SELFDESTRUCT immediately after the address, starting at 0x20 (32) in memory
                mstore8(0x20, 0xff)
                // this will always succeed because the contract creation code is
                // valid, and the address is valid because it is a 20-byte value
                if iszero(create(amount, 0x0b, 0x16)) {
                    // @dev For better gas estimation.
                    if iszero(gt(gas(), 1000000)) {
                        revert(0, 0)
                    }
                }
            }
        }
    }

    /**
     * @notice Returns whether or not the provided address `coreContract`
     * is an Art Blocks Engine core contract. Caches the result for future access.
     * @param coreContract Address of the core contract to check.
     */
    function isEngine(address coreContract) internal returns (bool) {
        IsEngineCache storage isEngineCache = getIsEngineCacheConfig(
            coreContract
        );
        // check cache, return early if cached
        if (isEngineCache.isCached) {
            return isEngineCache.isEngine;
        }
        // populate cache and return result
        bool isEngine_ = getV3CoreIsEngineView(coreContract);
        isEngineCache.isCached = true;
        isEngineCache.isEngine = isEngine_;
        return isEngine_;
    }

    /**
     * @notice Returns whether a V3 core contract is an Art Blocks Engine
     * contract or not. Return value of false indicates that the core is a
     * flagship contract. This function does not update the cache state for the
     * given V3 core contract.
     * @dev this function reverts if a core contract does not return the
     * expected number of return values from getPrimaryRevenueSplits() for
     * either a flagship or engine core contract.
     * @dev this function uses the length of the return data (in bytes) to
     * determine whether the core is an engine or not.
     * @param coreContract The address of the deployed core contract.
     */
    function getV3CoreIsEngineView(
        address coreContract
    ) internal view returns (bool) {
        // call getPrimaryRevenueSplits() on core contract
        bytes memory payload = abi.encodeWithSignature(
            "getPrimaryRevenueSplits(uint256,uint256)",
            0,
            0
        );
        (bool success, bytes memory returnData) = coreContract.staticcall(
            payload
        );
        require(success, "getPrimaryRevenueSplits() call failed");
        // determine whether core is engine or not, based on return data length
        uint256 returnDataLength = returnData.length;
        if (returnDataLength == 6 * 32) {
            // 6 32-byte words returned if flagship (not engine)
            // @dev 6 32-byte words are expected because the non-engine core
            // contracts return a payout address and uint256 payment value for
            // the artist, and artist's additional payee, and Art Blocks.
            // also note that per Solidity ABI encoding, the address return
            // values are padded to 32 bytes.

            return false;
        } else if (returnDataLength == 8 * 32) {
            // 8 32-byte words returned if engine
            // @dev 8 32-byte words are expected because the engine core
            // contracts return a payout address and uint256 payment value for
            // the artist, artist's additional payee, render provider
            // typically Art Blocks, and platform provider (partner).
            // also note that per Solidity ABI encoding, the address return
            // values are padded to 32 bytes.
            return true;
        }
        // unexpected return value length
        revert("Unexpected revenue split bytes");
    }

    /**
     * @notice Gets the currency address and symbol for the referenced
     * SplitFundsProjectConfig.
     * Only supports ERC20 tokens - returns currencySymbol of `UNCONFIG` if
     * `currencyAddress` is zero.
     * @param projectId Project ID to get config for
     * @param coreContract Core contract address to get config for
     * @return currencyAddress currency address for the referenced SplitFundsProjectConfig.
     * @return currencySymbol currency symbol for the referenced SplitFundsProjectConfig.
     */
    function getCurrencyInfoERC20(
        uint256 projectId,
        address coreContract
    )
        internal
        view
        returns (address currencyAddress, string memory currencySymbol)
    {
        SplitFundsProjectConfig
            storage splitFundsProjectConfig = getSplitFundsProjectConfig({
                projectId: projectId,
                coreContract: coreContract
            });
        currencyAddress = splitFundsProjectConfig.currencyAddress;
        // default to "UNCONFIG" if project currency address is initial value
        currencySymbol = currencyAddress == address(0)
            ? "UNCONFIG"
            : splitFundsProjectConfig.currencySymbol;
    }

    /**
     * @notice Gets the balance of `currencyAddress` ERC20 tokens for `walletAddress`.
     * @param currencyAddress ERC20 token address.
     * @param walletAddress wallet address.
     * @return balance Balance of ERC-20
     */
    function getERC20Balance(
        address currencyAddress,
        address walletAddress
    ) internal view returns (uint256) {
        return IERC20(currencyAddress).balanceOf(walletAddress);
    }

    /**
     * @notice Gets the allowance of `spenderAddress` to spend `walletAddress`'s
     * `currencyAddress` ERC20 tokens.
     * @param currencyAddress ERC20 token address.
     * @param walletAddress wallet address.
     * @param spenderAddress spender address.
     * @return allowance Allowance of ERC-20
     */
    function getERC20Allowance(
        address currencyAddress,
        address walletAddress,
        address spenderAddress
    ) internal view returns (uint256 allowance) {
        allowance = IERC20(currencyAddress).allowance({
            owner: walletAddress,
            spender: spenderAddress
        });
        return allowance;
    }

    /**
     * @notice Function validates that `msgSender` has approved the contract to spend at least
     * `pricePerToken` of `currencyAddress` ERC20 tokens, and that
     * `msgSender` has a balance of at least `pricePerToken` of
     * `currencyAddress` ERC20 tokens.
     * Reverts if insufficient allowance or balance.
     * @param msgSender Address of the message sender to validate.
     * @param currencyAddress Address of the ERC20 token to validate.
     * @param pricePerToken Price of token, in base units. For example,
     * if the ERC20 token has 6 decimals, an input value of `1_000_000` would
     * represent a price of `1.000000` tokens.
     */
    function validateERC20Approvals(
        address msgSender,
        address currencyAddress,
        uint256 pricePerToken
    ) private view {
        require(
            IERC20(currencyAddress).allowance({
                owner: msgSender,
                spender: address(this)
            }) >= pricePerToken,
            "Insufficient ERC20 allowance"
        );
        require(
            IERC20(currencyAddress).balanceOf(msgSender) >= pricePerToken,
            "Insufficient ERC20 balance"
        );
    }

    /**
     * @notice Sends ETH revenues between providers, artist, and artist's
     * additional payee. Reverts if any payment fails.
     * @dev This function pays priviliged addresses. DoS is acknowledged, and
     * mitigated by business practices, including end-to-end testing on
     * mainnet, and admin-accepted artist payment addresses.
     * @param platformProviderRevenue Platform Provider revenue.
     * @param platformProviderAddress Platform Provider address.
     * @param renderProviderRevenue Render Provider revenue.
     * @param renderProviderAddress Render Provider address.
     * @param artistRevenue Artist revenue.
     * @param artistAddress Artist address.
     * @param additionalPayeePrimaryRevenue Additional Payee revenue.
     * @param additionalPayeePrimaryAddress Additional Payee address.
     */
    function _sendPaymentsETH(
        uint256 platformProviderRevenue,
        address payable platformProviderAddress,
        uint256 renderProviderRevenue,
        address payable renderProviderAddress,
        uint256 artistRevenue,
        address payable artistAddress,
        uint256 additionalPayeePrimaryRevenue,
        address payable additionalPayeePrimaryAddress
    ) private {
        // Platform Provider payment (only possible if engine)
        if (platformProviderRevenue > 0) {
            (bool success, ) = platformProviderAddress.call{
                value: platformProviderRevenue
            }("");
            require(success, "Platform Provider payment failed");
        }
        // Render Provider / Art Blocks payment
        if (renderProviderRevenue > 0) {
            (bool success, ) = renderProviderAddress.call{
                value: renderProviderRevenue
            }("");
            require(success, "Render Provider payment failed");
        }
        // artist payment
        if (artistRevenue > 0) {
            (bool success, ) = artistAddress.call{value: artistRevenue}("");
            require(success, "Artist payment failed");
        }
        // additional payee payment
        if (additionalPayeePrimaryRevenue > 0) {
            (bool success, ) = additionalPayeePrimaryAddress.call{
                value: additionalPayeePrimaryRevenue
            }("");
            require(success, "Additional Payee payment failed");
        }
    }

    /**
     * @notice Sends ERC20 revenues between providers, artist, and artist's
     * additional payee. Reverts if any payment fails. All revenue values
     * should use base units. For example, if the ERC20 token has 6 decimals,
     * an input value of `1_000_000` would represent an amount of `1.000000`
     * tokens.
     * @dev This function relies on msg.sender, so it must be called from
     * the contract that is receiving the payment.
     * @param projectCurrency IERC20 payment token.
     * @param platformProviderRevenue Platform Provider revenue.
     * @param platformProviderAddress Platform Provider address.
     * @param renderProviderRevenue Render Provider revenue.
     * @param renderProviderAddress Render Provider address.
     * @param artistRevenue Artist revenue.
     * @param artistAddress Artist address.
     * @param additionalPayeePrimaryRevenue Additional Payee revenue.
     * @param additionalPayeePrimaryAddress Additional Payee address.
     */
    function _sendPaymentsERC20(
        IERC20 projectCurrency,
        uint256 platformProviderRevenue,
        address payable platformProviderAddress,
        uint256 renderProviderRevenue,
        address payable renderProviderAddress,
        uint256 artistRevenue,
        address payable artistAddress,
        uint256 additionalPayeePrimaryRevenue,
        address payable additionalPayeePrimaryAddress
    ) private {
        // Platform Provider payment (only possible if engine)
        if (platformProviderRevenue > 0) {
            require(
                projectCurrency.transferFrom({
                    from: msg.sender,
                    to: platformProviderAddress,
                    amount: platformProviderRevenue
                }),
                "Platform Provider payment failed"
            );
        }
        // Art Blocks payment
        if (renderProviderRevenue > 0) {
            require(
                projectCurrency.transferFrom({
                    from: msg.sender,
                    to: renderProviderAddress,
                    amount: renderProviderRevenue
                }),
                "Render Provider payment failed"
            );
        }
        // artist payment
        if (artistRevenue > 0) {
            require(
                projectCurrency.transferFrom({
                    from: msg.sender,
                    to: artistAddress,
                    amount: artistRevenue
                }),
                "Artist payment failed"
            );
        }
        // additional payee payment
        if (additionalPayeePrimaryRevenue > 0) {
            // @dev some ERC20 may not revert on transfer failure, so we
            // check the return value
            require(
                projectCurrency.transferFrom({
                    from: msg.sender,
                    to: additionalPayeePrimaryAddress,
                    amount: additionalPayeePrimaryRevenue
                }),
                "Additional Payee payment failed"
            );
        }
    }

    /**
     * @notice Loads the SplitFundsProjectConfig for a given project and core
     * contract.
     * @param projectId Project Id to get config for
     * @param coreContract Core contract address to get config for
     */
    function getSplitFundsProjectConfig(
        uint256 projectId,
        address coreContract
    ) internal view returns (SplitFundsProjectConfig storage) {
        return s().splitFundsProjectConfigs[coreContract][projectId];
    }

    /**
     * @notice Loads the IsEngineCache for a given core contract.
     * @param coreContract Core contract address to get config for
     */
    function getIsEngineCacheConfig(
        address coreContract
    ) internal view returns (IsEngineCache storage) {
        return s().isEngineCacheConfigs[coreContract];
    }

    /**
     * @notice Return the storage struct for reading and writing. This library
     * uses a diamond storage pattern when managing storage.
     * @return storageStruct The SetPriceLibStorage struct.
     */
    function s()
        internal
        pure
        returns (SplitFundsLibStorage storage storageStruct)
    {
        bytes32 position = SPLIT_FUNDS_LIB_STORAGE_POSITION;
        assembly ("memory-safe") {
            storageStruct.slot := position
        }
    }
}

// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.

pragma solidity ^0.8.0;

/**
 * @dev Library for packing multiple boolean values into a single uint256.
 * This is useful for storing a large number of bool values in a more compact
 * way than solidify's native bool type, which uses 8 bytes per bool.
 *
 * The implementation is similar to a BitMap, but function names are more
 * descriptive for packing and unpacking multiple bools.
 *
 * Note that the library may still be used in cases where less than 256 bools
 * are needed to be packed. For example, if <= 8 bools are needed, casting may
 * be used outside of the library for compatibility with any size uint.
 */
library PackedBools {
    function getBool(
        uint256 packedBool,
        uint8 index
    ) internal pure returns (bool) {
        uint256 mask = 1 << index;
        return packedBool & mask != 0;
    }

    function setBoolTrue(
        uint256 bitMap,
        uint8 index
    ) internal pure returns (uint256 newBitMap) {
        uint256 mask = 1 << index;
        return bitMap | mask;
    }

    function setBoolFalse(
        uint256 bitMap,
        uint8 index
    ) internal pure returns (uint256 newBitMap) {
        uint256 mask = 1 << index;
        return bitMap & ~mask;
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"minterFilter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"auctionBufferSeconds","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxAuctionExtraSeconds","type":"uint256"}],"name":"AuctionBufferTimeParamsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestampStart","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestampEnd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"basePrice","type":"uint256"},{"indexed":false,"internalType":"bool","name":"allowExtraTime","type":"bool"},{"indexed":false,"internalType":"bool","name":"adminArtistOnlyMintPeriodIfSellout","type":"bool"},{"indexed":false,"internalType":"uint256","name":"numTokensInAuction","type":"uint256"}],"name":"AuctionConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestampEnd","type":"uint256"}],"name":"AuctionTimestampEndUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"slotIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bidId","type":"uint256"},{"indexed":false,"internalType":"address","name":"bidder","type":"address"}],"name":"BidCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"bidId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"BidMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"bidId","type":"uint256"}],"name":"BidRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"bidId","type":"uint256"}],"name":"BidRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"bidId","type":"uint256"}],"name":"BidSettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"bidId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newSlotIndex","type":"uint256"}],"name":"BidToppedUp","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"ConfigValueSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"enum RAMLib.AdminMintingConstraint","name":"adminMintingConstraint","type":"uint8"}],"name":"ContractConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minAuctionDurationSeconds","type":"uint256"}],"name":"MinAuctionDurationSecondsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint24","name":"refundGasLimit","type":"uint24"}],"name":"MinterRefundGasLimitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"numSlots","type":"uint256"}],"name":"NumSlotsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"numTokensInAuction","type":"uint256"}],"name":"NumTokensInAuctionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"maxInvocations","type":"uint256"}],"name":"ProjectMaxInvocationsLimitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"projectId","type":"uint256"},{"indexed":true,"internalType":"address","name":"coreContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"TokenPurchased","type":"event"},{"inputs":[],"name":"MIN_AUCTION_DURATION_SECONDS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint8","name":"emergencyHoursToAdd","type":"uint8"}],"name":"adminAddEmergencyAuctionHours","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint24","name":"numTokensToMint","type":"uint24"}],"name":"adminArtistAutoMintTokensToWinners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint32[]","name":"bidIds","type":"uint32[]"}],"name":"adminArtistDirectMintTokensToWinners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint32[]","name":"bidIds","type":"uint32[]"}],"name":"adminArtistDirectRefundWinners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint24","name":"numBidsToRefund","type":"uint24"}],"name":"adminAutoRefundWinners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint32[]","name":"bidIds","type":"uint32[]"}],"name":"collectSettlements","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"}],"name":"contractConfigurationDetails","outputs":[{"internalType":"enum RAMLib.AdminMintingConstraint","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint16","name":"slotIndex","type":"uint16"}],"name":"createBid","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"getAuctionDetails","outputs":[{"internalType":"uint256","name":"auctionTimestampStart","type":"uint256"},{"internalType":"uint256","name":"auctionTimestampEnd","type":"uint256"},{"internalType":"uint256","name":"basePrice","type":"uint256"},{"internalType":"uint256","name":"numTokensInAuction","type":"uint256"},{"internalType":"uint256","name":"numBids","type":"uint256"},{"internalType":"uint256","name":"numBidsMintedTokens","type":"uint256"},{"internalType":"uint256","name":"numBidsErrorRefunded","type":"uint256"},{"internalType":"uint256","name":"minBidSlotIndex","type":"uint256"},{"internalType":"bool","name":"allowExtraTime","type":"bool"},{"internalType":"bool","name":"adminArtistOnlyMintPeriodIfSellout","type":"bool"},{"internalType":"bool","name":"revenuesCollected","type":"bool"},{"internalType":"enum RAMLib.ProjectMinterStates","name":"projectMinterState","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"getIsErrorE1","outputs":[{"internalType":"bool","name":"isError","type":"bool"},{"internalType":"uint256","name":"numBidsToRefund","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"getLowestBidValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"getMinimumNextBid","outputs":[{"internalType":"uint256","name":"minNextBidValueInWei","type":"uint256"},{"internalType":"uint256","name":"minNextBidSlotIndex","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"getPriceInfo","outputs":[{"internalType":"bool","name":"isConfigured","type":"bool"},{"internalType":"uint256","name":"tokenPriceInWei","type":"uint256"},{"internalType":"string","name":"currencySymbol","type":"string"},{"internalType":"address","name":"currencyAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"getProjectBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"}],"name":"isEngineView","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint24","name":"maxInvocations","type":"uint24"}],"name":"manuallyLimitProjectMaxInvocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"maxInvocationsProjectConfig","outputs":[{"components":[{"internalType":"bool","name":"maxHasBeenInvoked","type":"bool"},{"internalType":"uint24","name":"maxInvocations","type":"uint24"}],"internalType":"struct MaxInvocationsLib.MaxInvocationsProjectConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterConfigurationDetails","outputs":[{"internalType":"uint256","name":"minAuctionDurationSeconds","type":"uint256"},{"internalType":"uint256","name":"auctionBufferSeconds","type":"uint256"},{"internalType":"uint256","name":"maxAuctionExtraSeconds","type":"uint256"},{"internalType":"uint256","name":"maxAuctionAdminEmergencyExtensionHours","type":"uint256"},{"internalType":"uint256","name":"adminArtistOnlyMintTimeSeconds","type":"uint256"},{"internalType":"uint24","name":"minterRefundGasLimit","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterFilterAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterType","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"projectMaxHasBeenInvoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"projectMaxInvocations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"purchase","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"purchaseTo","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint40","name":"auctionTimestampEnd","type":"uint40"}],"name":"reduceAuctionLength","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint40","name":"auctionTimestampStart","type":"uint40"},{"internalType":"uint40","name":"auctionTimestampEnd","type":"uint40"},{"internalType":"uint256","name":"basePrice","type":"uint256"},{"internalType":"bool","name":"allowExtraTime","type":"bool"},{"internalType":"bool","name":"adminArtistOnlyMintPeriodIfSellout","type":"bool"}],"name":"setAuctionDetails","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"enum RAMLib.AdminMintingConstraint","name":"adminMintingConstraint","type":"uint8"}],"name":"setContractConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint16","name":"slotIndex","type":"uint16"}],"name":"slotIndexToBidValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"syncProjectMaxInvocationsToCore","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint32","name":"bidId","type":"uint32"},{"internalType":"uint16","name":"newSlotIndex","type":"uint16"}],"name":"topUpBid","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint24","name":"minterRefundGasLimit","type":"uint24"}],"name":"updateRefundGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint32[]","name":"bidIds","type":"uint32[]"}],"name":"winnerDirectMintTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint32[]","name":"bidIds","type":"uint32[]"}],"name":"winnerDirectRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"withdrawArtistAndAdminRevenues","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60c06040526001805462ffffff19166175301790553480156200002157600080fd5b5060405162005fe938038062005fe9833981016040819052620000449162000144565b60016000556001600160a01b038116608081905260a05260405161025881527f4ef0f5aca00c220a4058c318e0dd5526680003d4e6fceb9ad65087452a8313fa9060200160405180910390a160015460405162ffffff90911681527ff28602592f2788507e76a86523ec8d5ef26873807e258d4cae31571370c0e6c89060200160405180910390a16040805161012c8152610e1060208201527f0c92d75d9ae429e723cd03b00b1498b72fd1a30fd4337bf4cbdf14650599dd38910160405180910390a160405161020081527f93eac00dcf92d1cee4f4605f83f625533396c288555591736b62533e656beaf69060200160405180910390a15062000176565b6000602082840312156200015757600080fd5b81516001600160a01b03811681146200016f57600080fd5b9392505050565b60805160a051615e1c620001cd600039600081816108a8015281816109c801528181610a9901528181610b1201528181610cfe01528181610dde015261103c01526000818161067a0152610f420152615e1c6000f3fe60806040526004361061019e5760003560e01c806241673c146101a357806301000da7146101d6578063014053a014610206578063047fef49146102285780631290b96b1461026057806317e70c3c1461028057806340b790bd146102935780634869f3df146102ca5780634e8d8787146102ea57806363076680146102fd57806365e92cde1461034c57806366133e291461036c578063711ce3911461038c5780637ca340a6146103ac5780637e86d370146103cc5780638f13865e146103ec5780638fe8a90b1461040c5780639a94488a1461042c5780639b7b55851461046e578063a5514c7e14610484578063ae77c237146104a4578063b000e334146104b7578063b9daf3b5146104d7578063bf54d5fb146104f7578063c1b9281e14610517578063cce7c90914610537578063d009cb9d14610557578063d3ddabe61461058c578063d4f6d0e3146105cb578063d8b4e3ba146105f8578063d9bffbce14610618578063d9eb16db14610638578063dd85582f14610668578063e624db9f146106b4578063e9d1e8ac146106d4578063f5a759b71461070b575b600080fd5b3480156101af57600080fd5b506101c36101be366004615165565b61071e565b6040519081526020015b60405180910390f35b3480156101e257600080fd5b506101f66101f13660046151a3565b610754565b60405190151581526020016101cd565b34801561021257600080fd5b506102266102213660046151c0565b61078c565b005b34801561023457600080fd5b5061024861024336600461524b565b6107e2565b6040516101cd9c9b9a99989796959493929190615291565b34801561026c57600080fd5b5061022661027b36600461531f565b610826565b61022661028e366004615165565b610870565b34801561029f57600080fd5b506102b36102ae36600461524b565b610969565b6040805192151583526020830191909152016101cd565b3480156102d657600080fd5b506101c36102e536600461524b565b610982565b6101c36102f8366004615354565b610997565b34801561030957600080fd5b5060015460408051610258815261012c6020820152610e1091810191909152604860608201526203f480608082015262ffffff90911660a082015260c0016101cd565b34801561035857600080fd5b50610226610367366004615396565b6109f9565b34801561037857600080fd5b506102266103873660046153d3565b610a3e565b34801561039857600080fd5b506102266103a73660046151c0565b610a5f565b3480156103b857600080fd5b506102266103c73660046151c0565b610ac3565b3480156103d857600080fd5b506102266103e7366004615413565b610b3c565b3480156103f857600080fd5b506102266104073660046151c0565b610bc5565b34801561041857600080fd5b5061022661042736600461524b565b610c1c565b34801561043857600080fd5b5061044c61044736600461524b565b610c6a565b6040805182511515815260209283015162ffffff1692810192909252016101cd565b34801561047a57600080fd5b506101c361025881565b34801561049057600080fd5b5061022661049f3660046154a6565b610c7c565b6101c36104b236600461524b565b610ccd565b3480156104c357600080fd5b506102266104d23660046154a6565b610d2e565b3480156104e357600080fd5b506102266104f23660046154a6565b610d94565b34801561050357600080fd5b506101c361051236600461524b565b610e08565b34801561052357600080fd5b506102266105323660046151c0565b610e14565b34801561054357600080fd5b506101f661055236600461524b565b610e56565b34801561056357600080fd5b5061057761057236600461524b565b610e62565b604080519283526020830191909152016101cd565b34801561059857600080fd5b506105be60405180604001604052806006815260200165076302e302e360d41b81525081565b6040516101cd919061552b565b3480156105d757600080fd5b506105eb6105e63660046151a3565b610e7a565b6040516101cd919061553e565b34801561060457600080fd5b506101c361061336600461524b565b610e85565b34801561062457600080fd5b5061022661063336600461524b565b610ec1565b34801561064457600080fd5b5061065861065336600461524b565b610f00565b6040516101cd9493929190615558565b34801561067457600080fd5b5061069c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101cd565b3480156106c057600080fd5b506102266106cf366004615592565b610f3d565b3480156106e057600080fd5b506105be6040518060400160405280600b81526020016a04d696e74657252414d56360ac1b81525081565b6102266107193660046155c1565b611004565b60008061072b85856110f0565b60060154600160901b90046001600160581b0316905061074b8184611121565b95945050505050565b600080610760836111b7565b8054909150610100900460ff161561077c575460ff1692915050565b610785836111f0565b9392505050565b6002600054036107b75760405162461bcd60e51b81526004016107ae90615610565b60405180910390fd5b6002600055600180546107d79186918691869186919062ffffff16611366565b505060016000555050565b6000806000806000806000806000806000806107fe8e8e611568565b9b509b509b509b509b509b509b509b509b509b509b509b509295989b509295989b509295989b565b6002600054036108485760405162461bcd60e51b81526004016107ae90615610565b60026000556108588383336116c7565b610866838383610258611711565b5050600160005550565b6002600054036108925760405162461bcd60e51b81526004016107ae90615610565b6002600055604051635845de1f60e01b815230907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690635845de1f906108e79087908790600401615647565b602060405180830381865afa158015610904573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610928919061565e565b6001600160a01b03161461094e5760405162461bcd60e51b81526004016107ae9061567b565b600154610866908490849084903390349062ffffff16611951565b6000806109768484611c55565b50909590945092505050565b600061098e8383611cc2565b90505b92915050565b60006002600054036109bb5760405162461bcd60e51b81526004016107ae90615610565b60026000556109ec8484847f0000000000000000000000000000000000000000000000000000000000000000611ce2565b6001600055949350505050565b600260005403610a1b5760405162461bcd60e51b81526004016107ae90615610565b6002600055610a338233306332f4966f60e11b611efc565b610866838383611f57565b610a518233306366133e2960e01b611efc565b610a5b828261210e565b5050565b600260005403610a815760405162461bcd60e51b81526004016107ae90615610565b6002600055600180546107d7918691869186918691907f00000000000000000000000000000000000000000000000000000000000000009062ffffff16612199565b600260005403610ae55760405162461bcd60e51b81526004016107ae90615610565b6002600055610afe84843330633e51a05360e11b612368565b6001546107d79085908590859085906000907f00000000000000000000000000000000000000000000000000000000000000009062ffffff16612199565b600260005403610b5e5760405162461bcd60e51b81526004016107ae90615610565b6002600055610b6e8787336116c7565b610258610b7b86866156bc565b64ffffffffff161015610ba05760405162461bcd60e51b81526004016107ae906156da565b610bb787878787610bb0886123d1565b878761243d565b505060016000555050505050565b600260005403610be75760405162461bcd60e51b81526004016107ae90615610565b6002600055610c0084843330634789c32f60e11b612368565b6001546107d790859085908590859060009062ffffff16611366565b600260005403610c3e5760405162461bcd60e51b81526004016107ae90615610565b6002600055610c5782823330638fe8a90b60e01b612368565b610c61828261278e565b50506001600055565b610c7261511f565b61098e8383612943565b600260005403610c9e5760405162461bcd60e51b81526004016107ae90615610565b6002600055610cb68233306352a8a63f60e11b611efc565b6001546108669084908490849062ffffff16612973565b6000600260005403610cf15760405162461bcd60e51b81526004016107ae90615610565b6002600055610d223384847f0000000000000000000000000000000000000000000000000000000000000000611ce2565b60016000559392505050565b610d398383336116c7565b6000610d458484612b60565b90506000816004811115610d5b57610d5b61527b565b14610d785760405162461bcd60e51b81526004016107ae90615705565b610d83848484612c90565b610d8d8484612d49565b5050505050565b600260005403610db65760405162461bcd60e51b81526004016107ae90615610565b6002600055610dcf8383333063b9daf3b560e01b612368565b600154610866908490849084907f00000000000000000000000000000000000000000000000000000000000000009062ffffff16612dca565b600061098e8383612fdf565b600260005403610e365760405162461bcd60e51b81526004016107ae90615610565b60026000556001546107d7908590859085908590339062ffffff16613001565b600061098e83836130e5565b600080610e6f84846131bd565b909590945092505050565b600061099182613357565b600080610e928484613386565b9150506000610ea185856110f0565b60060154600160901b90046001600160581b0316905061074b8183611121565b60405162461bcd60e51b81526020600482015260146024820152731058dd1a5bdb881b9bdd081cdd5c1c1bdc9d195960621b60448201526064016107ae565b60008060606000610f11868661341a565b60408051808201909152600381526208aa8960eb1b602082015291989097509095506000945092505050565b610f707f0000000000000000000000000000000000000000000000000000000000000000333063e624db9f60e01b613554565b611b588162ffffff161015610fb85760405162461bcd60e51b815260206004820152600e60248201526d04f6e6c792067746520375f3030360941b60448201526064016107ae565b6001805462ffffff191662ffffff83169081179091556040519081527ff28602592f2788507e76a86523ec8d5ef26873807e258d4cae31571370c0e6c89060200160405180910390a150565b6002600054036110265760405162461bcd60e51b81526004016107ae90615610565b6002600055604051635845de1f60e01b815230907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690635845de1f9061107b9088908890600401615647565b602060405180830381865afa158015611098573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110bc919061565e565b6001600160a01b0316146110e25760405162461bcd60e51b81526004016107ae9061567b565b6107d78484848433346135a9565b60006110fa61382a565b6001600160a01b039290921660009081526020928352604080822094825293909252502090565b60006102008261ffff16106111485760405162461bcd60e51b81526004016107ae9061572f565b611155600861020061577b565b6111639061ffff841661577b565b6001600160581b038416901b905061117e600861020061577b565b61118b600861020061577b565b6111999061ffff851661578f565b6111a390836157a3565b6111ad919061577b565b61098e90826157ba565b6001600160a01b031660009081527f69f22dd730b53ff235fa96b4306a31bef5dad48fe4bac28b1ceebbce14401b396020526040902090565b6040516000602482018190526044820181905290819060640160408051601f198184030181529181526020820180516001600160e01b0316638639415b60e01b1790525190915060009081906001600160a01b038616906112529085906157cd565b600060405180830381855afa9150503d806000811461128d576040519150601f19603f3d011682016040523d82523d6000602084013e611292565b606091505b5091509150816112f25760405162461bcd60e51b815260206004820152602560248201527f6765745072696d617279526576656e756553706c69747328292063616c6c2066604482015264185a5b195960da1b60648201526084016107ae565b805160c08190036113095750600095945050505050565b806101000361131e5750600195945050505050565b60405162461bcd60e51b815260206004820152601e60248201527f556e657870656374656420726576656e75652073706c6974206279746573000060448201526064016107ae565b8260006113738888612b60565b905060038160048111156113895761138961527b565b146113a65760405162461bcd60e51b81526004016107ae906157e9565b6000806113b38a8a611c55565b5091509150816113d55760405162461bcd60e51b81526004016107ae9061581e565b808411156113f55760405162461bcd60e51b81526004016107ae90615848565b505050600061140488886110f0565b905060006114118261384e565b90506000805b8481101561151d5760008989838181106114335761143361587f565b90506020020160208101906114489190615895565b63ffffffff166000818152602087905260409020805491925090600160501b90046001600160a01b03168061148f5760405162461bcd60e51b81526004016107ae906158b0565b61149a8260016138b5565b806114ab57506114ab8260026138b5565b156114b857505050611515565b89156114e657336001600160a01b038216146114e65760405162461bcd60e51b81526004016107ae906158d8565b8154611504908f908f908990600160401b900461ffff16878e6138ce565b8461150e81615907565b9550505050505b600101611417565b50808360050160198282829054906101000a900462ffffff166115409190615929565b92506101000a81548162ffffff021916908362ffffff16021790555050505050505050505050565b6000806000806000806000806000806000806115848e8e612b60565b905060006115928f8f6110f0565b90508060060160009054906101000a900464ffffffffff1664ffffffffff169c5080600601600a9054906101000a900464ffffffffff1664ffffffffff169b508060060160129054906101000a90046001600160581b03166001600160581b03169a508060050160109054906101000a900462ffffff1662ffffff1699508060050160139054906101000a900462ffffff1662ffffff1698508060050160169054906101000a900462ffffff1662ffffff1697508060050160199054906101000a900462ffffff1662ffffff1696508060050160009054906101000a900461ffff1661ffff1695508060060160109054906101000a900460ff1694508060060160119054906101000a900460ff16935080600701600f9054906101000a900460ff169250509295989b509295989b509295989b565b6116d2838383613a28565b61170c5760405162461bcd60e51b815260206004820152600b60248201526a13db9b1e48105c9d1a5cdd60aa1b60448201526064016107ae565b505050565b600061171d85856110f0565b9050600161172b8686612b60565b600481111561173c5761173c61527b565b146117595760405162461bcd60e51b81526004016107ae90615945565b6006810154600160781b900460ff16156117b35760405162461bcd60e51b815260206004820152601b60248201527a273790383932bb34b7bab99030b236b4b71032bc3a32b739b4b7b760291b60448201526064016107ae565b6006810154600160281b810464ffffffffff908116600160501b90920416146117ee5760405162461bcd60e51b81526004016107ae90615970565b600681015464ffffffffff600160501b9091048116908416106118505760405162461bcd60e51b815260206004820152601a60248201527909edcd8f240e4cac8eac6ca40c2eac6e8d2dedc40d8cadccee8d60331b60448201526064016107ae565b600681015461186790839064ffffffffff166157ba565b8364ffffffffff161161188c5760405162461bcd60e51b81526004016107ae906156da565b428364ffffffffff16116118d95760405162461bcd60e51b81526020600482015260146024820152734f6e6c792066757475726520656e642074696d6560601b60448201526064016107ae565b600681018054600160281b600160781b031916600160501b64ffffffffff861690810264ffffffffff60281b191691909117600160281b919091021790556040516001600160a01b038516908690600080516020615da7833981519152906119429087906159a3565b60405180910390a35050505050565b600061195d87876110f0565b9050600161196b8888612b60565b600481111561197c5761197c61527b565b146119995760405162461bcd60e51b81526004016107ae90615945565b60068101546000906119bb90600160901b90046001600160581b031687611121565b9050808414611a0c5760405162461bcd60e51b815260206004820152601f60248201527f6d73672e76616c7565206d75737420657175616c20736c6f742076616c75650060448201526064016107ae565b600782018054859190600090611a2c9084906001600160781b03166159b5565b92506101000a8154816001600160781b0302191690836001600160781b031602179055508160050160139054906101000a900462ffffff1662ffffff16600003611a8657611a7a8888613ab1565b611a848888612d49565b505b6005820154600160801b900462ffffff1680611adb5760405162461bcd60e51b81526020600482015260146024820152732737903a37b5b2b7399034b71030bab1ba34b7b760611b60448201526064016107ae565b6005830154600160981b900462ffffff168111801590611c3a576000611b03858c8c89613b6c565b9050611b0f8188613d94565b611b545760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e74206269642076616c756560501b60448201526064016107ae565b6006850154600090600160801b900460ff168015611b9057506006860154611b8d9061012c90600160501b900464ffffffffff166159d5565b42115b90508015611c37576006860154611bcd90611bbc90610e1090600160281b900464ffffffffff166157ba565b611bc861012c426157ba565b613dea565b86600601600a6101000a81548164ffffffffff021916908364ffffffffff1602179055508a6001600160a01b03168c600080516020615da783398151915288600601600a9054906101000a900464ffffffffff16604051611c2e91906159a3565b60405180910390a35b50505b611c49848b8b8b8b6000613e00565b50505050505050505050565b600080600080611c6586866110f0565b90506000611c728261411f565b90506000611c80888861416c565b9050808211955085611c93576000611c9d565b611c9d81836159d5565b945085611cb357611cae82826159d5565b611cb6565b60005b93505050509250925092565b600080611ccf84846141d3565b54610100900462ffffff16949350505050565b600080611cef85856110f0565b90506000611cfd8686612b60565b90506003816004811115611d1357611d1361527b565b1480611d3057506004816004811115611d2e57611d2e61527b565b145b611d705760405162461bcd60e51b81526020600482015260116024820152704f6e6c792073746174652044206f72204560781b60448201526064016107ae565b6000611d7c8787611c55565b9250505060008111611dca5760405162461bcd60e51b815260206004820152601760248201527652656163686564206d617820696e766f636174696f6e7360481b60448201526064016107ae565b50506006810154600160901b90046001600160581b0316348114611e305760405162461bcd60e51b815260206004820152601f60248201527f4f6e6c792073656e642061756374696f6e20726573657276652070726963650060448201526064016107ae565b6040516117cd60e21b81526001600160a01b03851690615f3490611e5e908a908a908a9033906004016159e8565b6020604051808303816000875af1158015611e7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea19190615a13565b9250611eae8682876141fb565b846001600160a01b0316867fdfd0b295795ee66148297b0ff929867a78b799aca7c8def4666dfd1110e58070858a604051611eea929190615647565b60405180910390a35050949350505050565b611f08848484846143bc565b611f515760405162461bcd60e51b815260206004820152601a60248201527913db9b1e4810dbdc994810591b5a5b9050d308185b1b1bddd95960321b60448201526064016107ae565b50505050565b6000611f6384846110f0565b90506001611f718585612b60565b6004811115611f8257611f8261527b565b14611f9f5760405162461bcd60e51b81526004016107ae90615945565b6006810154600160281b810464ffffffffff908116600160501b9092041614611fda5760405162461bcd60e51b81526004016107ae90615970565b6006810154600090611ff7908490600160781b900460ff16615a2c565b905060488160ff16111561204b5760405162461bcd60e51b815260206004820152601b60248201527a09edcd8f240cadacae4cecadcc6f240d0deeae4e640d8e840dac2f602b1b60448201526064016107ae565b600061205c60ff8516610e10615a45565b600684015461207d9161ffff1690600160501b900464ffffffffff16615a63565b60068401805465ffffffffffff60501b1916600160781b60ff86160264ffffffffff60501b191617600160501b64ffffffffff84169081029190911764ffffffffff60281b1916600160281b919091021790556040519091506001600160a01b038616908790600080516020615da7833981519152906120fe9085906159a3565b60405180910390a3505050505050565b8061211761382a565b6001600160a01b03841660009081526001918201602052604090208054909160ff199091169083600281111561214f5761214f61527b565b0217905550816001600160a01b03167f7e87227cdb277003c1fb102daa540277f21bae680e463f9b0022e4726cb59ab08260405161218d919061553e565b60405180910390a25050565b60006121a588886110f0565b905060006121b28261384e565b90508560006121c18b8b612b60565b905060038160048111156121d7576121d761527b565b146121f45760405162461bcd60e51b81526004016107ae906157e9565b6121fd8461411f565b82111561221c5760405162461bcd60e51b81526004016107ae90615a81565b5060005b8181101561235b57600089898381811061223c5761223c61587f565b90506020020160208101906122519190615895565b63ffffffff166000818152602087905260409020805491925090600160501b90046001600160a01b0316806122985760405162461bcd60e51b81526004016107ae906158b0565b6122a38260016138b5565b806122b457506122b48260026138b5565b156122c157505050612353565b89156122ef57336001600160a01b038216146122ef5760405162461bcd60e51b81526004016107ae906158d8565b600587018054600160b01b900462ffffff1690601661230d83615907565b91906101000a81548162ffffff021916908362ffffff1602179055505061234f8e8e888560000160089054906101000a900461ffff1661ffff16878e8e614444565b5050505b600101612220565b5050505050505050505050565b612373858585613a28565b806123855750612385848484846143bc565b610d8d5760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c7920417274697374206f7220436f72652041646d696e2041434c00000060448201526064016107ae565b60006001600160581b038211156124395760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201526538206269747360d01b60648201526084016107ae565b5090565b600061244988886110f0565b905060006124578989612b60565b60048111156124685761246861527b565b146124855760405162461bcd60e51b81526004016107ae90615705565b66b1a2bc2ec50000846001600160581b031610156124e45760405162461bcd60e51b815260206004820152601c60248201527b09edcd8f240c4c2e6ca40e0e4d2c6ca40cee8ca40605c606a408aa8960231b60448201526064016107ae565b428664ffffffffff16116125315760405162461bcd60e51b81526020600482015260146024820152734f6e6c79206675747572652061756374696f6e7360601b60448201526064016107ae565b600061253c88613357565b905060018160028111156125525761255261527b565b036125a957826125a45760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c792061646d696e2d617274697374206d696e7420706572696f6400000060448201526064016107ae565b612610565b60028160028111156125bd576125bd61527b565b036126105782156126105760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79206e6f2061646d696e2d617274697374206d696e7420706572696f6460448201526064016107ae565b61261a8989613ab1565b868260060160006101000a81548164ffffffffff021916908364ffffffffff1602179055508582600601600a6101000a81548164ffffffffff021916908364ffffffffff160217905550858260060160056101000a81548164ffffffffff021916908364ffffffffff160217905550848260060160126101000a8154816001600160581b0302191690836001600160581b03160217905550838260060160106101000a81548160ff021916908315150217905550828260060160116101000a81548160ff02191690831515021790555060006126f68a8a612d49565b60058401805461ffff19166102001790556040805164ffffffffff8b811682528a1660208201526001600160581b038916918101919091528615156060820152851515608082015260a081018290529091506001600160a01b038a16908b907ff5182d65d8ff8a191c9e2f9f31fa4757a3e046c58e19c304b66f1766ec3993829060c00160405180910390a350505050505050505050565b600061279a83836110f0565b905060006127a88484612b60565b905060048160048111156127be576127be61527b565b146127fa5760405162461bcd60e51b815260206004820152600c60248201526b4f6e6c79207374617465204560a01b60448201526064016107ae565b6007820154600160781b900460ff16156128535760405162461bcd60e51b815260206004820152601a6024820152792932bb32b73ab2b99030b63932b0b23c903bb4ba34323930bbb760311b60448201526064016107ae565b60078201805460ff60781b1916600160781b17905560006128738361384e565b600584015490915060009061289490600160b01b900462ffffff16836157a3565b60078501805491925082916000906128b69084906001600160781b0316615ab8565b92506101000a8154816001600160781b0302191690836001600160781b031602179055506128e58682876141fb565b6040805177185d58dd1a5bdb94995d995b9d595cd0dbdb1b1958dd195960421b8152600160208201526001600160a01b0387169188917fbb5f9d4f49f83650956b76c40de0f082a06430bf0222e1b6b3f90a7a0f845c4d91016120fe565b61294b61511f565b6129558383611cc2565b62ffffff16602082015261296983836130e5565b1515815292915050565b600061297f8585612b60565b905060028160048111156129955761299561527b565b146129b25760405162461bcd60e51b81526004016107ae90615ad8565b6000806129bf8787611c55565b5091509150816129e15760405162461bcd60e51b81526004016107ae9061581e565b808562ffffff161115612a065760405162461bcd60e51b81526004016107ae90615848565b5050506000612a1585856110f0565b90506000612a228261384e565b6005830154600160401b9081900463ffffffff1660008181526020869052604081205493945090929190910461ffff16905b8662ffffff16811015612af45782600003612a8c57600585015461ffff16600081815260028701602052604090205493509150612aa4565b6000928352602085905260409092205463ffffffff16915b82600003612ada57612ac085612abb8460016157ba565b6144b8565b61ffff166000818152600287016020526040902054935091505b612aec8989868561ffff16878b6138ce565b600101612a54565b808560050160198282829054906101000a900462ffffff16612b169190615929565b92506101000a81548162ffffff021916908362ffffff160217905550828560050160086101000a81548163ffffffff021916908363ffffffff160217905550505050505050505050565b600080612b6d84846110f0565b600681015490915064ffffffffff1680158015904283119080612b8d5750805b15612b9f576000945050505050610991565b6006840154600160501b900464ffffffffff1642811080612bc95760019650505050505050610991565b600586015460009062ffffff600160981b8204811691612bfa91600160c81b8204811691600160b01b900416615929565b62ffffff161490508015612c18576004975050505050505050610991565b6006870154600090600160881b900460ff168015612c4f5750600588015462ffffff600160801b82048116600160981b9092041610155b8015612c665750612c636203f480856157ba565b42105b90508015612c7f57600298505050505050505050610991565b5060039a9950505050505050505050565b600080612c9d8585614573565b91509150808362ffffff161115612cc65760405162461bcd60e51b81526004016107ae90615afe565b818362ffffff161015612ceb5760405162461bcd60e51b81526004016107ae90615afe565b6000612cf786866141d3565b805462ffffff861685811463ffffffff19909216610100820260ff1916179190911782556040519081529091506001600160a01b038616908790600080516020615d87833981519152906020016120fe565b600080612d5684846110f0565b9050612d62848461416c565b60058201805462ffffff60801b1916600160801b62ffffff8416021790556040518181529092506001600160a01b0384169085907fe61b855b975f8c5d6051b121797e73a9c4e2b5fce1e570689013b3591427efa89060200160405180910390a35092915050565b6000612dd686866110f0565b90506000612de38261384e565b90506000612df18888612b60565b90506002816004811115612e0757612e0761527b565b14612e245760405162461bcd60e51b81526004016107ae90615ad8565b612e2d8361411f565b8662ffffff161115612e515760405162461bcd60e51b81526004016107ae90615a81565b506005820154600160201b900463ffffffff16600081815260208490526040812054600160401b900461ffff16905b8762ffffff16811015612f725782600003612ebe57600585015462010000900461ffff16600081815260018701602052604090205493509150612f5b565b600092835260208590526040832054600160201b900463ffffffff1692839003612f5b57612ef685612ef16001856159d5565b6145f1565b61ffff1691506102008210612f475760405162461bcd60e51b81526020600482015260176024820152761cdb1bdd081dda5d1a08189a59081b9bdd08199bdd5b99604a1b60448201526064016107ae565b600082815260018601602052604090205492505b612f6a8a8a8685878c8c614444565b600101612e80565b808560050160168282829054906101000a900462ffffff16612f949190615929565b92506101000a81548162ffffff021916908362ffffff160217905550828560050160046101000a81548163ffffffff021916908363ffffffff16021790555050505050505050505050565b600080612fec84846110f0565b600701546001600160781b0316949350505050565b600061300d87876110f0565b9050600061301b8888612b60565b905060028160048111156130315761303161527b565b148061304e5750600381600481111561304c5761304c61527b565b145b61308e5760405162461bcd60e51b815260206004820152601160248201527013db9b1e481cdd185d194810c81bdc8811607a1b60448201526064016107ae565b50600061309a8261384e565b90508460005b81811015611c49576130dd848b8b868c8c878181106130c1576130c161587f565b90506020020160208101906130d69190615895565b8b8b61468d565b6001016130a0565b6000806130f28484612b60565b905060008160048111156131085761310861527b565b0361315957600061311985856110f0565b600681015490915064ffffffffff1615613146576005810154600160801b900462ffffff16159250613153565b6131508585614760565b92505b506131b6565b600181600481111561316d5761316d61527b565b036131a357600061317e85856110f0565b6005015462ffffff600160801b82048116600160981b90920416101592506131b69050565b60006131af8585611c55565b1594505050505b5092915050565b60008060006131cc8585612b60565b905060006131da86866110f0565b905060008260048111156131f0576131f061527b565b0361326a57600681015464ffffffffff161515806132495760405162461bcd60e51b8152602060048201526016602482015275185d58dd1a5bdb881b9bdd0818dbdb999a59dd5c995960521b60448201526064016107ae565b506006810154600160901b90046001600160581b031693506000925061334e565b600581015462ffffff600160801b82048116600160981b909204161015600183600481111561329b5761329b61527b565b036132e95780156132c95760058201546132bc908890889061ffff16614779565b955061ffff16935061334c565b6006820154600160901b90046001600160581b031694506000935061334c565b80156133305760405162461bcd60e51b8152602060048201526016602482015275185d58dd1a5bdb88195b9919590b081cd95b1b1bdd5d60521b60448201526064016107ae565b6006820154600160901b90046001600160581b03169450600093505b505b50509250929050565b600061336161382a565b6001600160a01b03909216600090815260019290920160205250604090205460ff1690565b600080600061339585856110f0565b6005810154909150600160981b900462ffffff166133ea5760405162461bcd60e51b81526020600482015260126024820152712737903134b2399034b71030bab1ba34b7b760711b60448201526064016107ae565b600581015461ffff1660008181526002830160209081526040808320548352939052919091209590945092505050565b60008060006134298585612b60565b9050600061343786866110f0565b9050600082600481111561344d5761344d61527b565b0361348457600681015464ffffffffff16158015945061347f576006810154600160901b90046001600160581b031692505b61334e565b60058101546001945062ffffff600160801b82048116600160981b909204161015848360048111156134b8576134b861527b565b036134fe5780156134e25760058201546134d9908890889061ffff16614779565b945061334c9050565b6006820154600160901b90046001600160581b0316935061334c565b8015613533576006820154600583015461352c91600160901b90046001600160581b03169061ffff16611121565b935061334c565b50600601549295600160901b9093046001600160581b031694509192505050565b613560848484846143bc565b611f515760405162461bcd60e51b815260206004820152601a60248201527913db9b1e48135a5b9d195c919a5b1d195c8810591b5a5b9050d360321b60448201526064016107ae565b60006135b587876110f0565b63ffffffff86166000908152602082905260409020805491925090600160401b900461ffff1660016135e78a8a612b60565b60048111156135f8576135f861527b565b146136155760405162461bcd60e51b81526004016107ae90615945565b8154600160501b90046001600160a01b03166136705760405162461bcd60e51b815260206004820152601a60248201527942696420646e65202d207765726520796f75206f75746269643f60301b60448201526064016107ae565b81546001600160a01b03868116600160501b90920416146136d15760405162461bcd60e51b815260206004820152601b60248201527a13db9b1e48189a5919195c881bd988195e1a5cdd1a5b99c8189a59602a1b60448201526064016107ae565b60068301546000906136f390600160901b90046001600160581b031683611121565b905060006137188560060160129054906101000a90046001600160581b031689611121565b90508061372587846157ba565b1461376a5760405162461bcd60e51b8152602060048201526015602482015274696e636f72726563742061646465642076616c756560581b60448201526064016107ae565b505060078301805485919060009061378c9084906001600160781b03166159b5565b92506101000a8154816001600160781b0302191690836001600160781b031602179055506137c183828963ffffffff166147dc565b6137cf838a8a89898c613e00565b6040805163ffffffff8916815261ffff881660208201526001600160a01b038a16918b917fbcc60406b6a319d70c3feea7347029fbdfd768eecb981b0d2f6735b1e9633c5e91015b60405180910390a3505050505050505050565b7fa3fe13710cb596cbb938328f55db0dd5473d105cbcbbac73fbdbd94800043ab490565b600581015460009062ffffff600160801b82048116600160981b9092041610158061388d576006830154600160901b90046001600160581b0316610785565b6006830154600584015461078591600160901b90046001600160581b03169061ffff16611121565b815460009061098e90600160f01b900460ff1683614963565b60006138da87876110f0565b6000848152602082905260408120805492935091600160501b90046001600160a01b031690879061390b84826138b5565b6139425761391c8460006001614972565b50600684015460019061393f90600160901b90046001600160581b031689611121565b91505b61394f8460026001614972565b60078501805483919060009061396f9084906001600160781b0316615ab8565b92506101000a8154816001600160781b0302191690836001600160781b0316021790555061399e8383886149ce565b80156139d757896001600160a01b03168b600080516020615dc7833981519152896040516139ce91815260200190565b60405180910390a35b896001600160a01b03168b7fa59312996dc68b7f0224b341fed75d5afeb3bb20624107d37942d14f5942815689604051613a1391815260200190565b60405180910390a35050505050505050505050565b60405163a47d29cb60e01b8152600481018490526000906001600160a01b0384169063a47d29cb90602401602060405180830381865afa158015613a70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a94919061565e565b6001600160a01b0316826001600160a01b03161490509392505050565b6000613abd83836141d3565b9050613ac98383614a03565b15613ad85761170c8383614a36565b600080613ae58585614573565b84549193509150610100900462ffffff1681811115613b5157835483831463ffffffff1990911661010062ffffff85160260ff1916171784556040518281526001600160a01b038616908790600080516020615d878339815191529060200160405180910390a3613b64565b808310613b6457835460ff191660011784555b505050505050565b600584015461ffff16600081815260028601602052604081205490919080613bc05760405162461bcd60e51b81526020600482015260076024820152664e6f206269647360c81b60448201526064016107ae565b600081815260208881526040808320805461ffff8716855260028c01909352922063ffffffff82169081905560058a018054600160501b9093046001600160a01b031692600160981b900462ffffff16906013613c1c83615b2f565b91906101000a81548162ffffff021916908362ffffff160217905550508063ffffffff16600003613c945761ffff8516600081815260018c016020526040812055613c68908b90614ab0565b613c778a612abb876001615b4e565b60058b01805461ffff191661ffff92909216919091179055613cb8565b63ffffffff8116600090815260208b905260409020805463ffffffff60201b191690555b60068a0154613cd790600160901b90046001600160581b031686611121565b60078b0180549197508791600090613cf99084906001600160781b0316615ab8565b82546001600160781b039182166101009390930a928302919092021990911617905550600084815260208b90526040902080546001600160f81b0319169055613d438287896149ce565b876001600160a01b0316897f9d1a4511ddb5e2e227897ca79bbe1d71a9af123cd7c7bde8d2bcb292243310c986604051613d7f91815260200190565b60405180910390a35050505050949350505050565b60006706f05b59d3b20000831115613dc857612710613db58461280a6157a3565b613dbf919061577b565b82119050610991565b612710613dd7846129046157a3565b613de1919061577b565b90911192915050565b6000818310613df9578161098e565b5090919050565b63ffffffff8116158015613e4d57600587018054600c90613e2d90600160601b900463ffffffff16615b69565b91906101000a81548163ffffffff021916908363ffffffff160217905591505b60008760020160008661ffff1681526020019081526020016000205490506040518060a001604052808263ffffffff168152602001600063ffffffff1681526020018661ffff168152602001856001600160a01b03168152602001600060ff168152508860000160008563ffffffff16815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548161ffff021916908361ffff160217905550606082015181600001600a6101000a8154816001600160a01b0302191690836001600160a01b03160217905550608082015181600001601e6101000a81548160ff021916908360ff1602179055509050508263ffffffff168860020160008761ffff1681526020019081526020016000208190555080600003613fdc5761ffff85166000908152600189016020526040902063ffffffff84169055614007565b6000818152602089905260409020805463ffffffff60201b1916600160201b63ffffffff8616021790555b600588018054600160981b900462ffffff1690601361402583615907565b91906101000a81548162ffffff021916908362ffffff16021790555050806000036140b557614058888661ffff16614b0b565b600588015461ffff90811690861610156140805760058801805461ffff191661ffff87161790555b600588015461ffff62010000909104811690861611156140b55760058801805463ffff000019166201000061ffff8816021790555b8115614115576040805161ffff8716815263ffffffff851660208201526001600160a01b038681168284015291519188169189917fd278a5272d5c4a1fbd78fd96e1dc404c29b3e6fc9a525421b777de714354f5a5919081900360600190a35b5050505050505050565b60058101546000906141469062ffffff600160c81b8204811691600160b01b900416615929565b60058301546141619190600160981b900462ffffff16615b82565b62ffffff1692915050565b600080600061417b8585614573565b91509150600061418b86866141d3565b80549091506000906141a8908490610100900462ffffff16613dea565b90508084106141be576000945050505050610991565b6141c884826159d5565b979650505050505050565b60007f73e3af67f35f30fb72bbaaf6cc07b987364ed643c93ba201702e213464e877ee6110fa565b8160000361420857505050565b600061421382614b5a565b905060008060008060008060008088156142b157604051638639415b60e01b8152600481018d9052602481018c90526001600160a01b038b1690638639415b9060440161010060405180830381865afa158015614274573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142989190615b9e565b969e50949c50929a509098509650945092509050614331565b604051638639415b60e01b8152600481018d9052602481018c90526001600160a01b038b1690638639415b9060440160c060405180830381865afa1580156142fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143219190615c26565b949c50929a509096509450925090505b8a828561433e898c6157ba565b61434891906157ba565b61435291906157ba565b1461439e5760405162461bcd60e51b815260206004820152601c60248201527b496e76616c696420726576656e75652073706c697420746f74616c7360201b60448201526064016107ae565b6143ae86868a8a88888888614ba8565b505050505050505050505050565b60405163230448b160e01b81526001600160a01b03848116600483015283811660248301526001600160e01b0319831660448301526000919086169063230448b1906064016020604051808303816000875af1158015614420573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074b9190615c8f565b600061445088886110f0565b600085815260208290526040902090915061446d81600180614972565b805461448f908a908a908890600160501b90046001600160a01b031688614e4a565b61449a8160006138b5565b6144ad576144ad828a8a89898c89614f0a565b505050505050505050565b6000806101ff8361ffff1611156144e15760405162461bcd60e51b81526004016107ae90615cac565b600060ff8461ffff1611156145165760048501546144ff9085614ffa565b9250905061450f610100826157ba565b9050614551565b60038501546145259085614ffa565b925090508161455157600485015461453e906000614ffa565b9250905061454e610100826157ba565b90505b816145625761020092505050610991565b91506109919050565b505092915050565b604051630ea5613f60e01b81526004810183905260009081906001600160a01b03841690630ea5613f9060240160c060405180830381865afa1580156145bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145e19190615ce2565b5093989297509195505050505050565b6000806101ff8361ffff16111561461a5760405162461bcd60e51b81526004016107ae90615cac565b60006101008461ffff1610156146425760038501546146399085615060565b92509050614551565b60048501546146519085615060565b92509050614661610100826157ba565b9050816145515760038501546146789060ff615060565b92509050816145625761020092505050610991565b63ffffffff8316600090815260208890526040902080546001600160a01b03848116600160501b90920416146146f35760405162461bcd60e51b815260206004820152600b60248201526a27b7363c903134b23232b960a91b60448201526064016107ae565b6146fe8160006138b5565b156147415760405162461bcd60e51b815260206004820152601360248201527213db9b1e481d5b8b5cd95d1d1b195908189a59606a1b60448201526064016107ae565b805461411590899089908990600160401b900461ffff16888a88614f0a565b60008061476d84846141d3565b5460ff16949350505050565b600080600061478886866110f0565b6006810154909150600160901b90046001600160581b031660006147ac8287611121565b9050855b6001016147bd8382611121565b94506147c98286613d94565b156147b057989397509295505050505050565b60008181526020849052604081208054909163ffffffff80831692600160201b9004169082900361482a5761ffff85166000908152600187016020526040902063ffffffff8216905561485e565b63ffffffff80831660009081526020889052604090208054918316600160201b0263ffffffff60201b199092169190911790555b8063ffffffff1660000361488f5761ffff85166000908152600287016020526040902063ffffffff831690556148b8565b63ffffffff8181166000908152602088905260409020805463ffffffff19169184169190911790555b600586018054600160981b900462ffffff169060136148d683615b2f565b91906101000a81548162ffffff021916908362ffffff160217905550508163ffffffff16600014801561490d575063ffffffff8116155b15613b6457614920868661ffff16614ab0565b600586015461ffff808716911603613b645761494186612abb876001615b4e565b60058701805461ffff9290921661ffff19909216919091179055505050505050565b600160ff919091161b16151590565b80156149af578254600160ff8481169190911b600160f01b90920416175b835460ff91909116600160f01b0260ff60f01b19909116178355505050565b825461499090600160f01b900460ff1683600160ff919091161b191690565b600080600080858786f161170c57826000526073600b5360ff6020536016600b83f061170c57620f42405a1161170c57600080fd5b600080614a1084846141d3565b8054909150610100900462ffffff16158015614a2e5750805460ff16155b949350505050565b600080614a438484614573565b915091506000614a5385856141d3565b9050614a5e826150bb565b815460ff1962ffffff92909216610100029190911663ffffffff19909116178383141781556040516001600160a01b038516908690600080516020615d87833981519152906119429086815260200190565b6102008110614ad15760405162461bcd60e51b81526004016107ae9061572f565b610100811015614af3576003820154600160ff83161b19165b60038301555050565b6004820154600160ff83161b19165b60048301555050565b6102008110614b2c5760405162461bcd60e51b81526004016107ae9061572f565b610100811015614b48576003820154600160ff83161b17614aea565b6004820154600160ff83161b17614b02565b600080614b66836111b7565b8054909150610100900460ff1615614b82575460ff1692915050565b6000614b8d846111f0565b825481151561ffff1990911617610100179092555092915050565b8715614c53576000876001600160a01b03168960405160006040518083038185875af1925050503d8060008114614bfb576040519150601f19603f3d011682016040523d82523d6000602084013e614c00565b606091505b5050905080614c515760405162461bcd60e51b815260206004820181905260248201527f506c6174666f726d2050726f7669646572207061796d656e74206661696c656460448201526064016107ae565b505b8515614cfe576000856001600160a01b03168760405160006040518083038185875af1925050503d8060008114614ca6576040519150601f19603f3d011682016040523d82523d6000602084013e614cab565b606091505b5050905080614cfc5760405162461bcd60e51b815260206004820152601e60248201527f52656e6465722050726f7669646572207061796d656e74206661696c6564000060448201526064016107ae565b505b8315614da1576000836001600160a01b03168560405160006040518083038185875af1925050503d8060008114614d51576040519150601f19603f3d011682016040523d82523d6000602084013e614d56565b606091505b5050905080614d9f5760405162461bcd60e51b8152602060048201526015602482015274105c9d1a5cdd081c185e5b595b9d0819985a5b1959605a1b60448201526064016107ae565b505b8115614115576000816001600160a01b03168360405160006040518083038185875af1925050503d8060008114614df4576040519150601f19603f3d011682016040523d82523d6000602084013e614df9565b606091505b50509050806144ad5760405162461bcd60e51b815260206004820152601f60248201527f4164646974696f6e616c205061796565207061796d656e74206661696c65640060448201526064016107ae565b6040516117cd60e21b81526000906001600160a01b03831690615f3490614e7b9086908a908a9033906004016159e8565b6020604051808303816000875af1158015614e9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614ebe9190615a13565b6040805163ffffffff87168152602081018390529192506001600160a01b0387169188917f3c81bf6062ef45a4f3a4407699b008e79d9310eb22ba240838820f37535a928591016120fe565b63ffffffff8316600090815260208890526040812090614f2d9082906001614972565b600083614f518a60060160129054906101000a90046001600160581b031688611121565b614f5b91906159d5565b90508015614fc957600789018054829190600090614f839084906001600160781b0316615ab8565b92506101000a8154816001600160781b0302191690836001600160781b03160217905550614fc982600001600a9054906101000a90046001600160a01b031682856149ce565b60405163ffffffff861681526001600160a01b038816908990600080516020615dc783398151915290602001613817565b6000808260ff1684901c600003615017575060ff90506000615059565b8260ff1691505b60ff8210801561503557506150338483614963565b155b1561504c578161504481615d3d565b92505061501e565b6150568483614963565b90505b9250929050565b60008061506e8360ff615d56565b60ff1684901b60000361508657506000905080615059565b8260ff1691505b6000821180156150a457506150a28483614963565b155b1561504c57816150b381615d6f565b92505061508d565b600062ffffff8211156124395760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203260448201526534206269747360d01b60648201526084016107ae565b604080518082019091526000808252602082015290565b6001600160a01b038116811461514b57600080fd5b50565b803561ffff8116811461516057600080fd5b919050565b60008060006060848603121561517a57600080fd5b83359250602084013561518c81615136565b915061519a6040850161514e565b90509250925092565b6000602082840312156151b557600080fd5b813561078581615136565b600080600080606085870312156151d657600080fd5b8435935060208501356151e881615136565b925060408501356001600160401b038082111561520457600080fd5b818701915087601f83011261521857600080fd5b81358181111561522757600080fd5b8860208260051b850101111561523c57600080fd5b95989497505060200194505050565b6000806040838503121561525e57600080fd5b82359150602083013561527081615136565b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b6000610180820190508d82528c60208301528b60408301528a60608301528960808301528860a08301528760c08301528660e0830152851515610100830152841515610120830152831515610140830152600583106152f2576152f261527b565b826101608301529d9c50505050505050505050505050565b803564ffffffffff8116811461516057600080fd5b60008060006060848603121561533457600080fd5b83359250602084013561534681615136565b915061519a6040850161530a565b60008060006060848603121561536957600080fd5b833561537481615136565b925060208401359150604084013561538b81615136565b809150509250925092565b6000806000606084860312156153ab57600080fd5b8335925060208401356153bd81615136565b9150604084013560ff8116811461538b57600080fd5b600080604083850312156153e657600080fd5b82356153f181615136565b915060208301356003811061527057600080fd5b801515811461514b57600080fd5b600080600080600080600060e0888a03121561542e57600080fd5b87359650602088013561544081615136565b955061544e6040890161530a565b945061545c6060890161530a565b93506080880135925060a088013561547381615405565b915060c088013561548381615405565b8091505092959891949750929550565b803562ffffff8116811461516057600080fd5b6000806000606084860312156154bb57600080fd5b8335925060208401356154cd81615136565b915061519a60408501615493565b60005b838110156154f65781810151838201526020016154de565b50506000910152565b600081518084526155178160208601602086016154db565b601f01601f19169290920160200192915050565b60208152600061098e60208301846154ff565b60208101600383106155525761555261527b565b91905290565b841515815283602082015260806040820152600061557960808301856154ff565b905060018060a01b038316606083015295945050505050565b6000602082840312156155a457600080fd5b61098e82615493565b803563ffffffff8116811461516057600080fd5b600080600080608085870312156155d757600080fd5b8435935060208501356155e981615136565b92506155f7604086016155ad565b91506156056060860161514e565b905092959194509250565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b9182526001600160a01b0316602082015260400190565b60006020828403121561567057600080fd5b815161078581615136565b6020808252601190820152704d696e746572206e6f742061637469766560781b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b64ffffffffff8281168282160390808211156131b6576131b66156a6565b602080825260119082015270105d58dd1a5bdb881d1bdbc81cda1bdc9d607a1b604082015260600190565b60208082526010908201526f27b7363c9038393296b0bab1ba34b7b760811b604082015260600190565b6020808252601c908201527b4f6e6c7920736c6f7420696e646578206c74204e554d5f534c4f545360201b604082015260600190565b634e487b7160e01b600052601260045260246000fd5b60008261578a5761578a615765565b500490565b60008261579e5761579e615765565b500690565b8082028115828204841417610991576109916156a6565b80820180821115610991576109916156a6565b600082516157df8184602087016154db565b9190910192915050565b6020808252601b908201527a13db9b1e481c1bdcdd0b585d58dd1a5bdb881bdc195b881b5a5b9d602a1b604082015260600190565b60208082526010908201526f4f6e6c7920696e20737461746520453160801b604082015260600190565b6020808252601f908201527f6269647320746f20726566756e6420677420617661696c61626c652071747900604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156158a757600080fd5b61098e826155ad565b6020808252600e908201526d1a5b9d985b1a5908109a5908125160921b604082015260600190565b60208082526015908201527427b7363c9039b2b73232b91034b9903134b23232b960591b604082015260600190565b600062ffffff80831681810361591f5761591f6156a6565b6001019392505050565b62ffffff8181168382160190808211156131b6576131b66156a6565b60208082526011908201527027b7363c903634bb329030bab1ba34b7b760791b604082015260600190565b6020808252601990820152784e6f7420616c6c6f77656420696e2065787472612074696d6560381b604082015260600190565b64ffffffffff91909116815260200190565b6001600160781b038181168382160190808211156131b6576131b66156a6565b81810381811115610991576109916156a6565b6001600160a01b03948516815260208101939093529083166040830152909116606082015260800190565b600060208284031215615a2557600080fd5b5051919050565b60ff8181168382160190811115610991576109916156a6565b61ffff81811683821602808216919082811461456b5761456b6156a6565b64ffffffffff8181168382160190808211156131b6576131b66156a6565b6020808252601d908201527f746f6b656e7320746f206d696e7420677420746f6b656e73206f776564000000604082015260600190565b6001600160781b038281168282160390808211156131b6576131b66156a6565b6020808252600c908201526b4f6e6c79207374617465204360a01b604082015260600190565b602080825260179082015276496e76616c6964206d617820696e766f636174696f6e7360481b604082015260600190565b600062ffffff821680615b4457615b446156a6565b6000190192915050565b61ffff8181168382160190808211156131b6576131b66156a6565b600063ffffffff80831681810361591f5761591f6156a6565b62ffffff8281168282160390808211156131b6576131b66156a6565b600080600080600080600080610100898b031215615bbb57600080fd5b885197506020890151615bcd81615136565b60408a015160608b01519198509650615be581615136565b60808a015160a08b01519196509450615bfd81615136565b60c08a015160e08b01519194509250615c1581615136565b809150509295985092959890939650565b60008060008060008060c08789031215615c3f57600080fd5b865195506020870151615c5181615136565b604088015160608901519196509450615c6981615136565b608088015160a08901519194509250615c8181615136565b809150509295509295509295565b600060208284031215615ca157600080fd5b815161078581615405565b6020808252601c908201527b27b7363c9039ba30b93a1039b637ba1034b73232bc10363a101a989960211b604082015260600190565b60008060008060008060c08789031215615cfb57600080fd5b86519550602087015194506040870151615d1481615405565b6060880151909450615d2581615405565b608088015160a08901519194509250615c8181615405565b600060018201615d4f57615d4f6156a6565b5060010190565b60ff8281168282160390811115610991576109916156a6565b600081615d7e57615d7e6156a6565b50600019019056fe1c3e74a6c6fefbaeee166b031cd2016349c6c863d5188b67c9bbd0b0dcb2e237520e05d2a73ab549525f701daae4d8ac1897891bb1afca813602709aafc7a830e152fe384870d25a8d821270b8b8043fada748f138ab7f8512af64c65c726136a26469706673582212201be61649a635efb061b2ea27baa094eaa05101acc5df025a1d0875c9f9be0d3c64736f6c63430008160033000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b

Deployed Bytecode

0x60806040526004361061019e5760003560e01c806241673c146101a357806301000da7146101d6578063014053a014610206578063047fef49146102285780631290b96b1461026057806317e70c3c1461028057806340b790bd146102935780634869f3df146102ca5780634e8d8787146102ea57806363076680146102fd57806365e92cde1461034c57806366133e291461036c578063711ce3911461038c5780637ca340a6146103ac5780637e86d370146103cc5780638f13865e146103ec5780638fe8a90b1461040c5780639a94488a1461042c5780639b7b55851461046e578063a5514c7e14610484578063ae77c237146104a4578063b000e334146104b7578063b9daf3b5146104d7578063bf54d5fb146104f7578063c1b9281e14610517578063cce7c90914610537578063d009cb9d14610557578063d3ddabe61461058c578063d4f6d0e3146105cb578063d8b4e3ba146105f8578063d9bffbce14610618578063d9eb16db14610638578063dd85582f14610668578063e624db9f146106b4578063e9d1e8ac146106d4578063f5a759b71461070b575b600080fd5b3480156101af57600080fd5b506101c36101be366004615165565b61071e565b6040519081526020015b60405180910390f35b3480156101e257600080fd5b506101f66101f13660046151a3565b610754565b60405190151581526020016101cd565b34801561021257600080fd5b506102266102213660046151c0565b61078c565b005b34801561023457600080fd5b5061024861024336600461524b565b6107e2565b6040516101cd9c9b9a99989796959493929190615291565b34801561026c57600080fd5b5061022661027b36600461531f565b610826565b61022661028e366004615165565b610870565b34801561029f57600080fd5b506102b36102ae36600461524b565b610969565b6040805192151583526020830191909152016101cd565b3480156102d657600080fd5b506101c36102e536600461524b565b610982565b6101c36102f8366004615354565b610997565b34801561030957600080fd5b5060015460408051610258815261012c6020820152610e1091810191909152604860608201526203f480608082015262ffffff90911660a082015260c0016101cd565b34801561035857600080fd5b50610226610367366004615396565b6109f9565b34801561037857600080fd5b506102266103873660046153d3565b610a3e565b34801561039857600080fd5b506102266103a73660046151c0565b610a5f565b3480156103b857600080fd5b506102266103c73660046151c0565b610ac3565b3480156103d857600080fd5b506102266103e7366004615413565b610b3c565b3480156103f857600080fd5b506102266104073660046151c0565b610bc5565b34801561041857600080fd5b5061022661042736600461524b565b610c1c565b34801561043857600080fd5b5061044c61044736600461524b565b610c6a565b6040805182511515815260209283015162ffffff1692810192909252016101cd565b34801561047a57600080fd5b506101c361025881565b34801561049057600080fd5b5061022661049f3660046154a6565b610c7c565b6101c36104b236600461524b565b610ccd565b3480156104c357600080fd5b506102266104d23660046154a6565b610d2e565b3480156104e357600080fd5b506102266104f23660046154a6565b610d94565b34801561050357600080fd5b506101c361051236600461524b565b610e08565b34801561052357600080fd5b506102266105323660046151c0565b610e14565b34801561054357600080fd5b506101f661055236600461524b565b610e56565b34801561056357600080fd5b5061057761057236600461524b565b610e62565b604080519283526020830191909152016101cd565b34801561059857600080fd5b506105be60405180604001604052806006815260200165076302e302e360d41b81525081565b6040516101cd919061552b565b3480156105d757600080fd5b506105eb6105e63660046151a3565b610e7a565b6040516101cd919061553e565b34801561060457600080fd5b506101c361061336600461524b565b610e85565b34801561062457600080fd5b5061022661063336600461524b565b610ec1565b34801561064457600080fd5b5061065861065336600461524b565b610f00565b6040516101cd9493929190615558565b34801561067457600080fd5b5061069c7f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b81565b6040516001600160a01b0390911681526020016101cd565b3480156106c057600080fd5b506102266106cf366004615592565b610f3d565b3480156106e057600080fd5b506105be6040518060400160405280600b81526020016a04d696e74657252414d56360ac1b81525081565b6102266107193660046155c1565b611004565b60008061072b85856110f0565b60060154600160901b90046001600160581b0316905061074b8184611121565b95945050505050565b600080610760836111b7565b8054909150610100900460ff161561077c575460ff1692915050565b610785836111f0565b9392505050565b6002600054036107b75760405162461bcd60e51b81526004016107ae90615610565b60405180910390fd5b6002600055600180546107d79186918691869186919062ffffff16611366565b505060016000555050565b6000806000806000806000806000806000806107fe8e8e611568565b9b509b509b509b509b509b509b509b509b509b509b509b509295989b509295989b509295989b565b6002600054036108485760405162461bcd60e51b81526004016107ae90615610565b60026000556108588383336116c7565b610866838383610258611711565b5050600160005550565b6002600054036108925760405162461bcd60e51b81526004016107ae90615610565b6002600055604051635845de1f60e01b815230907f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b6001600160a01b031690635845de1f906108e79087908790600401615647565b602060405180830381865afa158015610904573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610928919061565e565b6001600160a01b03161461094e5760405162461bcd60e51b81526004016107ae9061567b565b600154610866908490849084903390349062ffffff16611951565b6000806109768484611c55565b50909590945092505050565b600061098e8383611cc2565b90505b92915050565b60006002600054036109bb5760405162461bcd60e51b81526004016107ae90615610565b60026000556109ec8484847f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b611ce2565b6001600055949350505050565b600260005403610a1b5760405162461bcd60e51b81526004016107ae90615610565b6002600055610a338233306332f4966f60e11b611efc565b610866838383611f57565b610a518233306366133e2960e01b611efc565b610a5b828261210e565b5050565b600260005403610a815760405162461bcd60e51b81526004016107ae90615610565b6002600055600180546107d7918691869186918691907f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b9062ffffff16612199565b600260005403610ae55760405162461bcd60e51b81526004016107ae90615610565b6002600055610afe84843330633e51a05360e11b612368565b6001546107d79085908590859085906000907f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b9062ffffff16612199565b600260005403610b5e5760405162461bcd60e51b81526004016107ae90615610565b6002600055610b6e8787336116c7565b610258610b7b86866156bc565b64ffffffffff161015610ba05760405162461bcd60e51b81526004016107ae906156da565b610bb787878787610bb0886123d1565b878761243d565b505060016000555050505050565b600260005403610be75760405162461bcd60e51b81526004016107ae90615610565b6002600055610c0084843330634789c32f60e11b612368565b6001546107d790859085908590859060009062ffffff16611366565b600260005403610c3e5760405162461bcd60e51b81526004016107ae90615610565b6002600055610c5782823330638fe8a90b60e01b612368565b610c61828261278e565b50506001600055565b610c7261511f565b61098e8383612943565b600260005403610c9e5760405162461bcd60e51b81526004016107ae90615610565b6002600055610cb68233306352a8a63f60e11b611efc565b6001546108669084908490849062ffffff16612973565b6000600260005403610cf15760405162461bcd60e51b81526004016107ae90615610565b6002600055610d223384847f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b611ce2565b60016000559392505050565b610d398383336116c7565b6000610d458484612b60565b90506000816004811115610d5b57610d5b61527b565b14610d785760405162461bcd60e51b81526004016107ae90615705565b610d83848484612c90565b610d8d8484612d49565b5050505050565b600260005403610db65760405162461bcd60e51b81526004016107ae90615610565b6002600055610dcf8383333063b9daf3b560e01b612368565b600154610866908490849084907f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b9062ffffff16612dca565b600061098e8383612fdf565b600260005403610e365760405162461bcd60e51b81526004016107ae90615610565b60026000556001546107d7908590859085908590339062ffffff16613001565b600061098e83836130e5565b600080610e6f84846131bd565b909590945092505050565b600061099182613357565b600080610e928484613386565b9150506000610ea185856110f0565b60060154600160901b90046001600160581b0316905061074b8183611121565b60405162461bcd60e51b81526020600482015260146024820152731058dd1a5bdb881b9bdd081cdd5c1c1bdc9d195960621b60448201526064016107ae565b60008060606000610f11868661341a565b60408051808201909152600381526208aa8960eb1b602082015291989097509095506000945092505050565b610f707f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b333063e624db9f60e01b613554565b611b588162ffffff161015610fb85760405162461bcd60e51b815260206004820152600e60248201526d04f6e6c792067746520375f3030360941b60448201526064016107ae565b6001805462ffffff191662ffffff83169081179091556040519081527ff28602592f2788507e76a86523ec8d5ef26873807e258d4cae31571370c0e6c89060200160405180910390a150565b6002600054036110265760405162461bcd60e51b81526004016107ae90615610565b6002600055604051635845de1f60e01b815230907f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b6001600160a01b031690635845de1f9061107b9088908890600401615647565b602060405180830381865afa158015611098573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110bc919061565e565b6001600160a01b0316146110e25760405162461bcd60e51b81526004016107ae9061567b565b6107d78484848433346135a9565b60006110fa61382a565b6001600160a01b039290921660009081526020928352604080822094825293909252502090565b60006102008261ffff16106111485760405162461bcd60e51b81526004016107ae9061572f565b611155600861020061577b565b6111639061ffff841661577b565b6001600160581b038416901b905061117e600861020061577b565b61118b600861020061577b565b6111999061ffff851661578f565b6111a390836157a3565b6111ad919061577b565b61098e90826157ba565b6001600160a01b031660009081527f69f22dd730b53ff235fa96b4306a31bef5dad48fe4bac28b1ceebbce14401b396020526040902090565b6040516000602482018190526044820181905290819060640160408051601f198184030181529181526020820180516001600160e01b0316638639415b60e01b1790525190915060009081906001600160a01b038616906112529085906157cd565b600060405180830381855afa9150503d806000811461128d576040519150601f19603f3d011682016040523d82523d6000602084013e611292565b606091505b5091509150816112f25760405162461bcd60e51b815260206004820152602560248201527f6765745072696d617279526576656e756553706c69747328292063616c6c2066604482015264185a5b195960da1b60648201526084016107ae565b805160c08190036113095750600095945050505050565b806101000361131e5750600195945050505050565b60405162461bcd60e51b815260206004820152601e60248201527f556e657870656374656420726576656e75652073706c6974206279746573000060448201526064016107ae565b8260006113738888612b60565b905060038160048111156113895761138961527b565b146113a65760405162461bcd60e51b81526004016107ae906157e9565b6000806113b38a8a611c55565b5091509150816113d55760405162461bcd60e51b81526004016107ae9061581e565b808411156113f55760405162461bcd60e51b81526004016107ae90615848565b505050600061140488886110f0565b905060006114118261384e565b90506000805b8481101561151d5760008989838181106114335761143361587f565b90506020020160208101906114489190615895565b63ffffffff166000818152602087905260409020805491925090600160501b90046001600160a01b03168061148f5760405162461bcd60e51b81526004016107ae906158b0565b61149a8260016138b5565b806114ab57506114ab8260026138b5565b156114b857505050611515565b89156114e657336001600160a01b038216146114e65760405162461bcd60e51b81526004016107ae906158d8565b8154611504908f908f908990600160401b900461ffff16878e6138ce565b8461150e81615907565b9550505050505b600101611417565b50808360050160198282829054906101000a900462ffffff166115409190615929565b92506101000a81548162ffffff021916908362ffffff16021790555050505050505050505050565b6000806000806000806000806000806000806115848e8e612b60565b905060006115928f8f6110f0565b90508060060160009054906101000a900464ffffffffff1664ffffffffff169c5080600601600a9054906101000a900464ffffffffff1664ffffffffff169b508060060160129054906101000a90046001600160581b03166001600160581b03169a508060050160109054906101000a900462ffffff1662ffffff1699508060050160139054906101000a900462ffffff1662ffffff1698508060050160169054906101000a900462ffffff1662ffffff1697508060050160199054906101000a900462ffffff1662ffffff1696508060050160009054906101000a900461ffff1661ffff1695508060060160109054906101000a900460ff1694508060060160119054906101000a900460ff16935080600701600f9054906101000a900460ff169250509295989b509295989b509295989b565b6116d2838383613a28565b61170c5760405162461bcd60e51b815260206004820152600b60248201526a13db9b1e48105c9d1a5cdd60aa1b60448201526064016107ae565b505050565b600061171d85856110f0565b9050600161172b8686612b60565b600481111561173c5761173c61527b565b146117595760405162461bcd60e51b81526004016107ae90615945565b6006810154600160781b900460ff16156117b35760405162461bcd60e51b815260206004820152601b60248201527a273790383932bb34b7bab99030b236b4b71032bc3a32b739b4b7b760291b60448201526064016107ae565b6006810154600160281b810464ffffffffff908116600160501b90920416146117ee5760405162461bcd60e51b81526004016107ae90615970565b600681015464ffffffffff600160501b9091048116908416106118505760405162461bcd60e51b815260206004820152601a60248201527909edcd8f240e4cac8eac6ca40c2eac6e8d2dedc40d8cadccee8d60331b60448201526064016107ae565b600681015461186790839064ffffffffff166157ba565b8364ffffffffff161161188c5760405162461bcd60e51b81526004016107ae906156da565b428364ffffffffff16116118d95760405162461bcd60e51b81526020600482015260146024820152734f6e6c792066757475726520656e642074696d6560601b60448201526064016107ae565b600681018054600160281b600160781b031916600160501b64ffffffffff861690810264ffffffffff60281b191691909117600160281b919091021790556040516001600160a01b038516908690600080516020615da7833981519152906119429087906159a3565b60405180910390a35050505050565b600061195d87876110f0565b9050600161196b8888612b60565b600481111561197c5761197c61527b565b146119995760405162461bcd60e51b81526004016107ae90615945565b60068101546000906119bb90600160901b90046001600160581b031687611121565b9050808414611a0c5760405162461bcd60e51b815260206004820152601f60248201527f6d73672e76616c7565206d75737420657175616c20736c6f742076616c75650060448201526064016107ae565b600782018054859190600090611a2c9084906001600160781b03166159b5565b92506101000a8154816001600160781b0302191690836001600160781b031602179055508160050160139054906101000a900462ffffff1662ffffff16600003611a8657611a7a8888613ab1565b611a848888612d49565b505b6005820154600160801b900462ffffff1680611adb5760405162461bcd60e51b81526020600482015260146024820152732737903a37b5b2b7399034b71030bab1ba34b7b760611b60448201526064016107ae565b6005830154600160981b900462ffffff168111801590611c3a576000611b03858c8c89613b6c565b9050611b0f8188613d94565b611b545760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e74206269642076616c756560501b60448201526064016107ae565b6006850154600090600160801b900460ff168015611b9057506006860154611b8d9061012c90600160501b900464ffffffffff166159d5565b42115b90508015611c37576006860154611bcd90611bbc90610e1090600160281b900464ffffffffff166157ba565b611bc861012c426157ba565b613dea565b86600601600a6101000a81548164ffffffffff021916908364ffffffffff1602179055508a6001600160a01b03168c600080516020615da783398151915288600601600a9054906101000a900464ffffffffff16604051611c2e91906159a3565b60405180910390a35b50505b611c49848b8b8b8b6000613e00565b50505050505050505050565b600080600080611c6586866110f0565b90506000611c728261411f565b90506000611c80888861416c565b9050808211955085611c93576000611c9d565b611c9d81836159d5565b945085611cb357611cae82826159d5565b611cb6565b60005b93505050509250925092565b600080611ccf84846141d3565b54610100900462ffffff16949350505050565b600080611cef85856110f0565b90506000611cfd8686612b60565b90506003816004811115611d1357611d1361527b565b1480611d3057506004816004811115611d2e57611d2e61527b565b145b611d705760405162461bcd60e51b81526020600482015260116024820152704f6e6c792073746174652044206f72204560781b60448201526064016107ae565b6000611d7c8787611c55565b9250505060008111611dca5760405162461bcd60e51b815260206004820152601760248201527652656163686564206d617820696e766f636174696f6e7360481b60448201526064016107ae565b50506006810154600160901b90046001600160581b0316348114611e305760405162461bcd60e51b815260206004820152601f60248201527f4f6e6c792073656e642061756374696f6e20726573657276652070726963650060448201526064016107ae565b6040516117cd60e21b81526001600160a01b03851690615f3490611e5e908a908a908a9033906004016159e8565b6020604051808303816000875af1158015611e7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea19190615a13565b9250611eae8682876141fb565b846001600160a01b0316867fdfd0b295795ee66148297b0ff929867a78b799aca7c8def4666dfd1110e58070858a604051611eea929190615647565b60405180910390a35050949350505050565b611f08848484846143bc565b611f515760405162461bcd60e51b815260206004820152601a60248201527913db9b1e4810dbdc994810591b5a5b9050d308185b1b1bddd95960321b60448201526064016107ae565b50505050565b6000611f6384846110f0565b90506001611f718585612b60565b6004811115611f8257611f8261527b565b14611f9f5760405162461bcd60e51b81526004016107ae90615945565b6006810154600160281b810464ffffffffff908116600160501b9092041614611fda5760405162461bcd60e51b81526004016107ae90615970565b6006810154600090611ff7908490600160781b900460ff16615a2c565b905060488160ff16111561204b5760405162461bcd60e51b815260206004820152601b60248201527a09edcd8f240cadacae4cecadcc6f240d0deeae4e640d8e840dac2f602b1b60448201526064016107ae565b600061205c60ff8516610e10615a45565b600684015461207d9161ffff1690600160501b900464ffffffffff16615a63565b60068401805465ffffffffffff60501b1916600160781b60ff86160264ffffffffff60501b191617600160501b64ffffffffff84169081029190911764ffffffffff60281b1916600160281b919091021790556040519091506001600160a01b038616908790600080516020615da7833981519152906120fe9085906159a3565b60405180910390a3505050505050565b8061211761382a565b6001600160a01b03841660009081526001918201602052604090208054909160ff199091169083600281111561214f5761214f61527b565b0217905550816001600160a01b03167f7e87227cdb277003c1fb102daa540277f21bae680e463f9b0022e4726cb59ab08260405161218d919061553e565b60405180910390a25050565b60006121a588886110f0565b905060006121b28261384e565b90508560006121c18b8b612b60565b905060038160048111156121d7576121d761527b565b146121f45760405162461bcd60e51b81526004016107ae906157e9565b6121fd8461411f565b82111561221c5760405162461bcd60e51b81526004016107ae90615a81565b5060005b8181101561235b57600089898381811061223c5761223c61587f565b90506020020160208101906122519190615895565b63ffffffff166000818152602087905260409020805491925090600160501b90046001600160a01b0316806122985760405162461bcd60e51b81526004016107ae906158b0565b6122a38260016138b5565b806122b457506122b48260026138b5565b156122c157505050612353565b89156122ef57336001600160a01b038216146122ef5760405162461bcd60e51b81526004016107ae906158d8565b600587018054600160b01b900462ffffff1690601661230d83615907565b91906101000a81548162ffffff021916908362ffffff1602179055505061234f8e8e888560000160089054906101000a900461ffff1661ffff16878e8e614444565b5050505b600101612220565b5050505050505050505050565b612373858585613a28565b806123855750612385848484846143bc565b610d8d5760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c7920417274697374206f7220436f72652041646d696e2041434c00000060448201526064016107ae565b60006001600160581b038211156124395760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201526538206269747360d01b60648201526084016107ae565b5090565b600061244988886110f0565b905060006124578989612b60565b60048111156124685761246861527b565b146124855760405162461bcd60e51b81526004016107ae90615705565b66b1a2bc2ec50000846001600160581b031610156124e45760405162461bcd60e51b815260206004820152601c60248201527b09edcd8f240c4c2e6ca40e0e4d2c6ca40cee8ca40605c606a408aa8960231b60448201526064016107ae565b428664ffffffffff16116125315760405162461bcd60e51b81526020600482015260146024820152734f6e6c79206675747572652061756374696f6e7360601b60448201526064016107ae565b600061253c88613357565b905060018160028111156125525761255261527b565b036125a957826125a45760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c792061646d696e2d617274697374206d696e7420706572696f6400000060448201526064016107ae565b612610565b60028160028111156125bd576125bd61527b565b036126105782156126105760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79206e6f2061646d696e2d617274697374206d696e7420706572696f6460448201526064016107ae565b61261a8989613ab1565b868260060160006101000a81548164ffffffffff021916908364ffffffffff1602179055508582600601600a6101000a81548164ffffffffff021916908364ffffffffff160217905550858260060160056101000a81548164ffffffffff021916908364ffffffffff160217905550848260060160126101000a8154816001600160581b0302191690836001600160581b03160217905550838260060160106101000a81548160ff021916908315150217905550828260060160116101000a81548160ff02191690831515021790555060006126f68a8a612d49565b60058401805461ffff19166102001790556040805164ffffffffff8b811682528a1660208201526001600160581b038916918101919091528615156060820152851515608082015260a081018290529091506001600160a01b038a16908b907ff5182d65d8ff8a191c9e2f9f31fa4757a3e046c58e19c304b66f1766ec3993829060c00160405180910390a350505050505050505050565b600061279a83836110f0565b905060006127a88484612b60565b905060048160048111156127be576127be61527b565b146127fa5760405162461bcd60e51b815260206004820152600c60248201526b4f6e6c79207374617465204560a01b60448201526064016107ae565b6007820154600160781b900460ff16156128535760405162461bcd60e51b815260206004820152601a6024820152792932bb32b73ab2b99030b63932b0b23c903bb4ba34323930bbb760311b60448201526064016107ae565b60078201805460ff60781b1916600160781b17905560006128738361384e565b600584015490915060009061289490600160b01b900462ffffff16836157a3565b60078501805491925082916000906128b69084906001600160781b0316615ab8565b92506101000a8154816001600160781b0302191690836001600160781b031602179055506128e58682876141fb565b6040805177185d58dd1a5bdb94995d995b9d595cd0dbdb1b1958dd195960421b8152600160208201526001600160a01b0387169188917fbb5f9d4f49f83650956b76c40de0f082a06430bf0222e1b6b3f90a7a0f845c4d91016120fe565b61294b61511f565b6129558383611cc2565b62ffffff16602082015261296983836130e5565b1515815292915050565b600061297f8585612b60565b905060028160048111156129955761299561527b565b146129b25760405162461bcd60e51b81526004016107ae90615ad8565b6000806129bf8787611c55565b5091509150816129e15760405162461bcd60e51b81526004016107ae9061581e565b808562ffffff161115612a065760405162461bcd60e51b81526004016107ae90615848565b5050506000612a1585856110f0565b90506000612a228261384e565b6005830154600160401b9081900463ffffffff1660008181526020869052604081205493945090929190910461ffff16905b8662ffffff16811015612af45782600003612a8c57600585015461ffff16600081815260028701602052604090205493509150612aa4565b6000928352602085905260409092205463ffffffff16915b82600003612ada57612ac085612abb8460016157ba565b6144b8565b61ffff166000818152600287016020526040902054935091505b612aec8989868561ffff16878b6138ce565b600101612a54565b808560050160198282829054906101000a900462ffffff16612b169190615929565b92506101000a81548162ffffff021916908362ffffff160217905550828560050160086101000a81548163ffffffff021916908363ffffffff160217905550505050505050505050565b600080612b6d84846110f0565b600681015490915064ffffffffff1680158015904283119080612b8d5750805b15612b9f576000945050505050610991565b6006840154600160501b900464ffffffffff1642811080612bc95760019650505050505050610991565b600586015460009062ffffff600160981b8204811691612bfa91600160c81b8204811691600160b01b900416615929565b62ffffff161490508015612c18576004975050505050505050610991565b6006870154600090600160881b900460ff168015612c4f5750600588015462ffffff600160801b82048116600160981b9092041610155b8015612c665750612c636203f480856157ba565b42105b90508015612c7f57600298505050505050505050610991565b5060039a9950505050505050505050565b600080612c9d8585614573565b91509150808362ffffff161115612cc65760405162461bcd60e51b81526004016107ae90615afe565b818362ffffff161015612ceb5760405162461bcd60e51b81526004016107ae90615afe565b6000612cf786866141d3565b805462ffffff861685811463ffffffff19909216610100820260ff1916179190911782556040519081529091506001600160a01b038616908790600080516020615d87833981519152906020016120fe565b600080612d5684846110f0565b9050612d62848461416c565b60058201805462ffffff60801b1916600160801b62ffffff8416021790556040518181529092506001600160a01b0384169085907fe61b855b975f8c5d6051b121797e73a9c4e2b5fce1e570689013b3591427efa89060200160405180910390a35092915050565b6000612dd686866110f0565b90506000612de38261384e565b90506000612df18888612b60565b90506002816004811115612e0757612e0761527b565b14612e245760405162461bcd60e51b81526004016107ae90615ad8565b612e2d8361411f565b8662ffffff161115612e515760405162461bcd60e51b81526004016107ae90615a81565b506005820154600160201b900463ffffffff16600081815260208490526040812054600160401b900461ffff16905b8762ffffff16811015612f725782600003612ebe57600585015462010000900461ffff16600081815260018701602052604090205493509150612f5b565b600092835260208590526040832054600160201b900463ffffffff1692839003612f5b57612ef685612ef16001856159d5565b6145f1565b61ffff1691506102008210612f475760405162461bcd60e51b81526020600482015260176024820152761cdb1bdd081dda5d1a08189a59081b9bdd08199bdd5b99604a1b60448201526064016107ae565b600082815260018601602052604090205492505b612f6a8a8a8685878c8c614444565b600101612e80565b808560050160168282829054906101000a900462ffffff16612f949190615929565b92506101000a81548162ffffff021916908362ffffff160217905550828560050160046101000a81548163ffffffff021916908363ffffffff16021790555050505050505050505050565b600080612fec84846110f0565b600701546001600160781b0316949350505050565b600061300d87876110f0565b9050600061301b8888612b60565b905060028160048111156130315761303161527b565b148061304e5750600381600481111561304c5761304c61527b565b145b61308e5760405162461bcd60e51b815260206004820152601160248201527013db9b1e481cdd185d194810c81bdc8811607a1b60448201526064016107ae565b50600061309a8261384e565b90508460005b81811015611c49576130dd848b8b868c8c878181106130c1576130c161587f565b90506020020160208101906130d69190615895565b8b8b61468d565b6001016130a0565b6000806130f28484612b60565b905060008160048111156131085761310861527b565b0361315957600061311985856110f0565b600681015490915064ffffffffff1615613146576005810154600160801b900462ffffff16159250613153565b6131508585614760565b92505b506131b6565b600181600481111561316d5761316d61527b565b036131a357600061317e85856110f0565b6005015462ffffff600160801b82048116600160981b90920416101592506131b69050565b60006131af8585611c55565b1594505050505b5092915050565b60008060006131cc8585612b60565b905060006131da86866110f0565b905060008260048111156131f0576131f061527b565b0361326a57600681015464ffffffffff161515806132495760405162461bcd60e51b8152602060048201526016602482015275185d58dd1a5bdb881b9bdd0818dbdb999a59dd5c995960521b60448201526064016107ae565b506006810154600160901b90046001600160581b031693506000925061334e565b600581015462ffffff600160801b82048116600160981b909204161015600183600481111561329b5761329b61527b565b036132e95780156132c95760058201546132bc908890889061ffff16614779565b955061ffff16935061334c565b6006820154600160901b90046001600160581b031694506000935061334c565b80156133305760405162461bcd60e51b8152602060048201526016602482015275185d58dd1a5bdb88195b9919590b081cd95b1b1bdd5d60521b60448201526064016107ae565b6006820154600160901b90046001600160581b03169450600093505b505b50509250929050565b600061336161382a565b6001600160a01b03909216600090815260019290920160205250604090205460ff1690565b600080600061339585856110f0565b6005810154909150600160981b900462ffffff166133ea5760405162461bcd60e51b81526020600482015260126024820152712737903134b2399034b71030bab1ba34b7b760711b60448201526064016107ae565b600581015461ffff1660008181526002830160209081526040808320548352939052919091209590945092505050565b60008060006134298585612b60565b9050600061343786866110f0565b9050600082600481111561344d5761344d61527b565b0361348457600681015464ffffffffff16158015945061347f576006810154600160901b90046001600160581b031692505b61334e565b60058101546001945062ffffff600160801b82048116600160981b909204161015848360048111156134b8576134b861527b565b036134fe5780156134e25760058201546134d9908890889061ffff16614779565b945061334c9050565b6006820154600160901b90046001600160581b0316935061334c565b8015613533576006820154600583015461352c91600160901b90046001600160581b03169061ffff16611121565b935061334c565b50600601549295600160901b9093046001600160581b031694509192505050565b613560848484846143bc565b611f515760405162461bcd60e51b815260206004820152601a60248201527913db9b1e48135a5b9d195c919a5b1d195c8810591b5a5b9050d360321b60448201526064016107ae565b60006135b587876110f0565b63ffffffff86166000908152602082905260409020805491925090600160401b900461ffff1660016135e78a8a612b60565b60048111156135f8576135f861527b565b146136155760405162461bcd60e51b81526004016107ae90615945565b8154600160501b90046001600160a01b03166136705760405162461bcd60e51b815260206004820152601a60248201527942696420646e65202d207765726520796f75206f75746269643f60301b60448201526064016107ae565b81546001600160a01b03868116600160501b90920416146136d15760405162461bcd60e51b815260206004820152601b60248201527a13db9b1e48189a5919195c881bd988195e1a5cdd1a5b99c8189a59602a1b60448201526064016107ae565b60068301546000906136f390600160901b90046001600160581b031683611121565b905060006137188560060160129054906101000a90046001600160581b031689611121565b90508061372587846157ba565b1461376a5760405162461bcd60e51b8152602060048201526015602482015274696e636f72726563742061646465642076616c756560581b60448201526064016107ae565b505060078301805485919060009061378c9084906001600160781b03166159b5565b92506101000a8154816001600160781b0302191690836001600160781b031602179055506137c183828963ffffffff166147dc565b6137cf838a8a89898c613e00565b6040805163ffffffff8916815261ffff881660208201526001600160a01b038a16918b917fbcc60406b6a319d70c3feea7347029fbdfd768eecb981b0d2f6735b1e9633c5e91015b60405180910390a3505050505050505050565b7fa3fe13710cb596cbb938328f55db0dd5473d105cbcbbac73fbdbd94800043ab490565b600581015460009062ffffff600160801b82048116600160981b9092041610158061388d576006830154600160901b90046001600160581b0316610785565b6006830154600584015461078591600160901b90046001600160581b03169061ffff16611121565b815460009061098e90600160f01b900460ff1683614963565b60006138da87876110f0565b6000848152602082905260408120805492935091600160501b90046001600160a01b031690879061390b84826138b5565b6139425761391c8460006001614972565b50600684015460019061393f90600160901b90046001600160581b031689611121565b91505b61394f8460026001614972565b60078501805483919060009061396f9084906001600160781b0316615ab8565b92506101000a8154816001600160781b0302191690836001600160781b0316021790555061399e8383886149ce565b80156139d757896001600160a01b03168b600080516020615dc7833981519152896040516139ce91815260200190565b60405180910390a35b896001600160a01b03168b7fa59312996dc68b7f0224b341fed75d5afeb3bb20624107d37942d14f5942815689604051613a1391815260200190565b60405180910390a35050505050505050505050565b60405163a47d29cb60e01b8152600481018490526000906001600160a01b0384169063a47d29cb90602401602060405180830381865afa158015613a70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a94919061565e565b6001600160a01b0316826001600160a01b03161490509392505050565b6000613abd83836141d3565b9050613ac98383614a03565b15613ad85761170c8383614a36565b600080613ae58585614573565b84549193509150610100900462ffffff1681811115613b5157835483831463ffffffff1990911661010062ffffff85160260ff1916171784556040518281526001600160a01b038616908790600080516020615d878339815191529060200160405180910390a3613b64565b808310613b6457835460ff191660011784555b505050505050565b600584015461ffff16600081815260028601602052604081205490919080613bc05760405162461bcd60e51b81526020600482015260076024820152664e6f206269647360c81b60448201526064016107ae565b600081815260208881526040808320805461ffff8716855260028c01909352922063ffffffff82169081905560058a018054600160501b9093046001600160a01b031692600160981b900462ffffff16906013613c1c83615b2f565b91906101000a81548162ffffff021916908362ffffff160217905550508063ffffffff16600003613c945761ffff8516600081815260018c016020526040812055613c68908b90614ab0565b613c778a612abb876001615b4e565b60058b01805461ffff191661ffff92909216919091179055613cb8565b63ffffffff8116600090815260208b905260409020805463ffffffff60201b191690555b60068a0154613cd790600160901b90046001600160581b031686611121565b60078b0180549197508791600090613cf99084906001600160781b0316615ab8565b82546001600160781b039182166101009390930a928302919092021990911617905550600084815260208b90526040902080546001600160f81b0319169055613d438287896149ce565b876001600160a01b0316897f9d1a4511ddb5e2e227897ca79bbe1d71a9af123cd7c7bde8d2bcb292243310c986604051613d7f91815260200190565b60405180910390a35050505050949350505050565b60006706f05b59d3b20000831115613dc857612710613db58461280a6157a3565b613dbf919061577b565b82119050610991565b612710613dd7846129046157a3565b613de1919061577b565b90911192915050565b6000818310613df9578161098e565b5090919050565b63ffffffff8116158015613e4d57600587018054600c90613e2d90600160601b900463ffffffff16615b69565b91906101000a81548163ffffffff021916908363ffffffff160217905591505b60008760020160008661ffff1681526020019081526020016000205490506040518060a001604052808263ffffffff168152602001600063ffffffff1681526020018661ffff168152602001856001600160a01b03168152602001600060ff168152508860000160008563ffffffff16815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548161ffff021916908361ffff160217905550606082015181600001600a6101000a8154816001600160a01b0302191690836001600160a01b03160217905550608082015181600001601e6101000a81548160ff021916908360ff1602179055509050508263ffffffff168860020160008761ffff1681526020019081526020016000208190555080600003613fdc5761ffff85166000908152600189016020526040902063ffffffff84169055614007565b6000818152602089905260409020805463ffffffff60201b1916600160201b63ffffffff8616021790555b600588018054600160981b900462ffffff1690601361402583615907565b91906101000a81548162ffffff021916908362ffffff16021790555050806000036140b557614058888661ffff16614b0b565b600588015461ffff90811690861610156140805760058801805461ffff191661ffff87161790555b600588015461ffff62010000909104811690861611156140b55760058801805463ffff000019166201000061ffff8816021790555b8115614115576040805161ffff8716815263ffffffff851660208201526001600160a01b038681168284015291519188169189917fd278a5272d5c4a1fbd78fd96e1dc404c29b3e6fc9a525421b777de714354f5a5919081900360600190a35b5050505050505050565b60058101546000906141469062ffffff600160c81b8204811691600160b01b900416615929565b60058301546141619190600160981b900462ffffff16615b82565b62ffffff1692915050565b600080600061417b8585614573565b91509150600061418b86866141d3565b80549091506000906141a8908490610100900462ffffff16613dea565b90508084106141be576000945050505050610991565b6141c884826159d5565b979650505050505050565b60007f73e3af67f35f30fb72bbaaf6cc07b987364ed643c93ba201702e213464e877ee6110fa565b8160000361420857505050565b600061421382614b5a565b905060008060008060008060008088156142b157604051638639415b60e01b8152600481018d9052602481018c90526001600160a01b038b1690638639415b9060440161010060405180830381865afa158015614274573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142989190615b9e565b969e50949c50929a509098509650945092509050614331565b604051638639415b60e01b8152600481018d9052602481018c90526001600160a01b038b1690638639415b9060440160c060405180830381865afa1580156142fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143219190615c26565b949c50929a509096509450925090505b8a828561433e898c6157ba565b61434891906157ba565b61435291906157ba565b1461439e5760405162461bcd60e51b815260206004820152601c60248201527b496e76616c696420726576656e75652073706c697420746f74616c7360201b60448201526064016107ae565b6143ae86868a8a88888888614ba8565b505050505050505050505050565b60405163230448b160e01b81526001600160a01b03848116600483015283811660248301526001600160e01b0319831660448301526000919086169063230448b1906064016020604051808303816000875af1158015614420573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061074b9190615c8f565b600061445088886110f0565b600085815260208290526040902090915061446d81600180614972565b805461448f908a908a908890600160501b90046001600160a01b031688614e4a565b61449a8160006138b5565b6144ad576144ad828a8a89898c89614f0a565b505050505050505050565b6000806101ff8361ffff1611156144e15760405162461bcd60e51b81526004016107ae90615cac565b600060ff8461ffff1611156145165760048501546144ff9085614ffa565b9250905061450f610100826157ba565b9050614551565b60038501546145259085614ffa565b925090508161455157600485015461453e906000614ffa565b9250905061454e610100826157ba565b90505b816145625761020092505050610991565b91506109919050565b505092915050565b604051630ea5613f60e01b81526004810183905260009081906001600160a01b03841690630ea5613f9060240160c060405180830381865afa1580156145bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145e19190615ce2565b5093989297509195505050505050565b6000806101ff8361ffff16111561461a5760405162461bcd60e51b81526004016107ae90615cac565b60006101008461ffff1610156146425760038501546146399085615060565b92509050614551565b60048501546146519085615060565b92509050614661610100826157ba565b9050816145515760038501546146789060ff615060565b92509050816145625761020092505050610991565b63ffffffff8316600090815260208890526040902080546001600160a01b03848116600160501b90920416146146f35760405162461bcd60e51b815260206004820152600b60248201526a27b7363c903134b23232b960a91b60448201526064016107ae565b6146fe8160006138b5565b156147415760405162461bcd60e51b815260206004820152601360248201527213db9b1e481d5b8b5cd95d1d1b195908189a59606a1b60448201526064016107ae565b805461411590899089908990600160401b900461ffff16888a88614f0a565b60008061476d84846141d3565b5460ff16949350505050565b600080600061478886866110f0565b6006810154909150600160901b90046001600160581b031660006147ac8287611121565b9050855b6001016147bd8382611121565b94506147c98286613d94565b156147b057989397509295505050505050565b60008181526020849052604081208054909163ffffffff80831692600160201b9004169082900361482a5761ffff85166000908152600187016020526040902063ffffffff8216905561485e565b63ffffffff80831660009081526020889052604090208054918316600160201b0263ffffffff60201b199092169190911790555b8063ffffffff1660000361488f5761ffff85166000908152600287016020526040902063ffffffff831690556148b8565b63ffffffff8181166000908152602088905260409020805463ffffffff19169184169190911790555b600586018054600160981b900462ffffff169060136148d683615b2f565b91906101000a81548162ffffff021916908362ffffff160217905550508163ffffffff16600014801561490d575063ffffffff8116155b15613b6457614920868661ffff16614ab0565b600586015461ffff808716911603613b645761494186612abb876001615b4e565b60058701805461ffff9290921661ffff19909216919091179055505050505050565b600160ff919091161b16151590565b80156149af578254600160ff8481169190911b600160f01b90920416175b835460ff91909116600160f01b0260ff60f01b19909116178355505050565b825461499090600160f01b900460ff1683600160ff919091161b191690565b600080600080858786f161170c57826000526073600b5360ff6020536016600b83f061170c57620f42405a1161170c57600080fd5b600080614a1084846141d3565b8054909150610100900462ffffff16158015614a2e5750805460ff16155b949350505050565b600080614a438484614573565b915091506000614a5385856141d3565b9050614a5e826150bb565b815460ff1962ffffff92909216610100029190911663ffffffff19909116178383141781556040516001600160a01b038516908690600080516020615d87833981519152906119429086815260200190565b6102008110614ad15760405162461bcd60e51b81526004016107ae9061572f565b610100811015614af3576003820154600160ff83161b19165b60038301555050565b6004820154600160ff83161b19165b60048301555050565b6102008110614b2c5760405162461bcd60e51b81526004016107ae9061572f565b610100811015614b48576003820154600160ff83161b17614aea565b6004820154600160ff83161b17614b02565b600080614b66836111b7565b8054909150610100900460ff1615614b82575460ff1692915050565b6000614b8d846111f0565b825481151561ffff1990911617610100179092555092915050565b8715614c53576000876001600160a01b03168960405160006040518083038185875af1925050503d8060008114614bfb576040519150601f19603f3d011682016040523d82523d6000602084013e614c00565b606091505b5050905080614c515760405162461bcd60e51b815260206004820181905260248201527f506c6174666f726d2050726f7669646572207061796d656e74206661696c656460448201526064016107ae565b505b8515614cfe576000856001600160a01b03168760405160006040518083038185875af1925050503d8060008114614ca6576040519150601f19603f3d011682016040523d82523d6000602084013e614cab565b606091505b5050905080614cfc5760405162461bcd60e51b815260206004820152601e60248201527f52656e6465722050726f7669646572207061796d656e74206661696c6564000060448201526064016107ae565b505b8315614da1576000836001600160a01b03168560405160006040518083038185875af1925050503d8060008114614d51576040519150601f19603f3d011682016040523d82523d6000602084013e614d56565b606091505b5050905080614d9f5760405162461bcd60e51b8152602060048201526015602482015274105c9d1a5cdd081c185e5b595b9d0819985a5b1959605a1b60448201526064016107ae565b505b8115614115576000816001600160a01b03168360405160006040518083038185875af1925050503d8060008114614df4576040519150601f19603f3d011682016040523d82523d6000602084013e614df9565b606091505b50509050806144ad5760405162461bcd60e51b815260206004820152601f60248201527f4164646974696f6e616c205061796565207061796d656e74206661696c65640060448201526064016107ae565b6040516117cd60e21b81526000906001600160a01b03831690615f3490614e7b9086908a908a9033906004016159e8565b6020604051808303816000875af1158015614e9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614ebe9190615a13565b6040805163ffffffff87168152602081018390529192506001600160a01b0387169188917f3c81bf6062ef45a4f3a4407699b008e79d9310eb22ba240838820f37535a928591016120fe565b63ffffffff8316600090815260208890526040812090614f2d9082906001614972565b600083614f518a60060160129054906101000a90046001600160581b031688611121565b614f5b91906159d5565b90508015614fc957600789018054829190600090614f839084906001600160781b0316615ab8565b92506101000a8154816001600160781b0302191690836001600160781b03160217905550614fc982600001600a9054906101000a90046001600160a01b031682856149ce565b60405163ffffffff861681526001600160a01b038816908990600080516020615dc783398151915290602001613817565b6000808260ff1684901c600003615017575060ff90506000615059565b8260ff1691505b60ff8210801561503557506150338483614963565b155b1561504c578161504481615d3d565b92505061501e565b6150568483614963565b90505b9250929050565b60008061506e8360ff615d56565b60ff1684901b60000361508657506000905080615059565b8260ff1691505b6000821180156150a457506150a28483614963565b155b1561504c57816150b381615d6f565b92505061508d565b600062ffffff8211156124395760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203260448201526534206269747360d01b60648201526084016107ae565b604080518082019091526000808252602082015290565b6001600160a01b038116811461514b57600080fd5b50565b803561ffff8116811461516057600080fd5b919050565b60008060006060848603121561517a57600080fd5b83359250602084013561518c81615136565b915061519a6040850161514e565b90509250925092565b6000602082840312156151b557600080fd5b813561078581615136565b600080600080606085870312156151d657600080fd5b8435935060208501356151e881615136565b925060408501356001600160401b038082111561520457600080fd5b818701915087601f83011261521857600080fd5b81358181111561522757600080fd5b8860208260051b850101111561523c57600080fd5b95989497505060200194505050565b6000806040838503121561525e57600080fd5b82359150602083013561527081615136565b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b6000610180820190508d82528c60208301528b60408301528a60608301528960808301528860a08301528760c08301528660e0830152851515610100830152841515610120830152831515610140830152600583106152f2576152f261527b565b826101608301529d9c50505050505050505050505050565b803564ffffffffff8116811461516057600080fd5b60008060006060848603121561533457600080fd5b83359250602084013561534681615136565b915061519a6040850161530a565b60008060006060848603121561536957600080fd5b833561537481615136565b925060208401359150604084013561538b81615136565b809150509250925092565b6000806000606084860312156153ab57600080fd5b8335925060208401356153bd81615136565b9150604084013560ff8116811461538b57600080fd5b600080604083850312156153e657600080fd5b82356153f181615136565b915060208301356003811061527057600080fd5b801515811461514b57600080fd5b600080600080600080600060e0888a03121561542e57600080fd5b87359650602088013561544081615136565b955061544e6040890161530a565b945061545c6060890161530a565b93506080880135925060a088013561547381615405565b915060c088013561548381615405565b8091505092959891949750929550565b803562ffffff8116811461516057600080fd5b6000806000606084860312156154bb57600080fd5b8335925060208401356154cd81615136565b915061519a60408501615493565b60005b838110156154f65781810151838201526020016154de565b50506000910152565b600081518084526155178160208601602086016154db565b601f01601f19169290920160200192915050565b60208152600061098e60208301846154ff565b60208101600383106155525761555261527b565b91905290565b841515815283602082015260806040820152600061557960808301856154ff565b905060018060a01b038316606083015295945050505050565b6000602082840312156155a457600080fd5b61098e82615493565b803563ffffffff8116811461516057600080fd5b600080600080608085870312156155d757600080fd5b8435935060208501356155e981615136565b92506155f7604086016155ad565b91506156056060860161514e565b905092959194509250565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b9182526001600160a01b0316602082015260400190565b60006020828403121561567057600080fd5b815161078581615136565b6020808252601190820152704d696e746572206e6f742061637469766560781b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b64ffffffffff8281168282160390808211156131b6576131b66156a6565b602080825260119082015270105d58dd1a5bdb881d1bdbc81cda1bdc9d607a1b604082015260600190565b60208082526010908201526f27b7363c9038393296b0bab1ba34b7b760811b604082015260600190565b6020808252601c908201527b4f6e6c7920736c6f7420696e646578206c74204e554d5f534c4f545360201b604082015260600190565b634e487b7160e01b600052601260045260246000fd5b60008261578a5761578a615765565b500490565b60008261579e5761579e615765565b500690565b8082028115828204841417610991576109916156a6565b80820180821115610991576109916156a6565b600082516157df8184602087016154db565b9190910192915050565b6020808252601b908201527a13db9b1e481c1bdcdd0b585d58dd1a5bdb881bdc195b881b5a5b9d602a1b604082015260600190565b60208082526010908201526f4f6e6c7920696e20737461746520453160801b604082015260600190565b6020808252601f908201527f6269647320746f20726566756e6420677420617661696c61626c652071747900604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156158a757600080fd5b61098e826155ad565b6020808252600e908201526d1a5b9d985b1a5908109a5908125160921b604082015260600190565b60208082526015908201527427b7363c9039b2b73232b91034b9903134b23232b960591b604082015260600190565b600062ffffff80831681810361591f5761591f6156a6565b6001019392505050565b62ffffff8181168382160190808211156131b6576131b66156a6565b60208082526011908201527027b7363c903634bb329030bab1ba34b7b760791b604082015260600190565b6020808252601990820152784e6f7420616c6c6f77656420696e2065787472612074696d6560381b604082015260600190565b64ffffffffff91909116815260200190565b6001600160781b038181168382160190808211156131b6576131b66156a6565b81810381811115610991576109916156a6565b6001600160a01b03948516815260208101939093529083166040830152909116606082015260800190565b600060208284031215615a2557600080fd5b5051919050565b60ff8181168382160190811115610991576109916156a6565b61ffff81811683821602808216919082811461456b5761456b6156a6565b64ffffffffff8181168382160190808211156131b6576131b66156a6565b6020808252601d908201527f746f6b656e7320746f206d696e7420677420746f6b656e73206f776564000000604082015260600190565b6001600160781b038281168282160390808211156131b6576131b66156a6565b6020808252600c908201526b4f6e6c79207374617465204360a01b604082015260600190565b602080825260179082015276496e76616c6964206d617820696e766f636174696f6e7360481b604082015260600190565b600062ffffff821680615b4457615b446156a6565b6000190192915050565b61ffff8181168382160190808211156131b6576131b66156a6565b600063ffffffff80831681810361591f5761591f6156a6565b62ffffff8281168282160390808211156131b6576131b66156a6565b600080600080600080600080610100898b031215615bbb57600080fd5b885197506020890151615bcd81615136565b60408a015160608b01519198509650615be581615136565b60808a015160a08b01519196509450615bfd81615136565b60c08a015160e08b01519194509250615c1581615136565b809150509295985092959890939650565b60008060008060008060c08789031215615c3f57600080fd5b865195506020870151615c5181615136565b604088015160608901519196509450615c6981615136565b608088015160a08901519194509250615c8181615136565b809150509295509295509295565b600060208284031215615ca157600080fd5b815161078581615405565b6020808252601c908201527b27b7363c9039ba30b93a1039b637ba1034b73232bc10363a101a989960211b604082015260600190565b60008060008060008060c08789031215615cfb57600080fd5b86519550602087015194506040870151615d1481615405565b6060880151909450615d2581615405565b608088015160a08901519194509250615c8181615405565b600060018201615d4f57615d4f6156a6565b5060010190565b60ff8281168282160390811115610991576109916156a6565b600081615d7e57615d7e6156a6565b50600019019056fe1c3e74a6c6fefbaeee166b031cd2016349c6c863d5188b67c9bbd0b0dcb2e237520e05d2a73ab549525f701daae4d8ac1897891bb1afca813602709aafc7a830e152fe384870d25a8d821270b8b8043fada748f138ab7f8512af64c65c726136a26469706673582212201be61649a635efb061b2ea27baa094eaa05101acc5df025a1d0875c9f9be0d3c64736f6c63430008160033

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

000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b

-----Decoded View---------------
Arg [0] : minterFilter (address): 0xa2ccfE293bc2CDD78D8166a82D1e18cD2148122b

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
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.