// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

// File: @openzeppelin/contracts/security/ReentrancyGuard.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

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

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



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

pragma solidity ^0.8.0;

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

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

// File: @openzeppelin/contracts/utils/cryptography/MerkleProof.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.2) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates merkle trees that are safe
 * against this attack out of the box.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

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

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

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

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * _Available since v4.7._
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

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

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

        if (totalHashes > 0) {
            require(proofPos == proofLen, "MerkleProof: invalid multiproof");
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

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

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

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

        if (totalHashes > 0) {
            require(proofPos == proofLen, "MerkleProof: invalid multiproof");
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

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

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

// File: contracts/DraftPicker.sol



//SPDX-License-Identifier: MIT
/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                           @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@          @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@           @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@              @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@              @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@           @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                           @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
/**
 * @title DraftPicker
 * @author @brougkr
 * @notice A Smart Contract To Facilitate Draft Pick Claims
 */
pragma solidity 0.8.19;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import { IMP } from "./IMP.sol";
contract DraftPicker is Ownable, ReentrancyGuard
{
    struct DraftPick
    {
        string _Name;              // [0] -> _Name 
        address _MintPass;         // [1] -> _MintPass (MintPass To Be Burned)
        address _MintedWork;       // [2] -> _MintedWork (NFT To Be Claimed)
        address _Operator;         // [3] -> _Operator (Address That NFTs Are Pulling From)
        address _ArtistAddress;    // [3] -> _ArtistAddress (Artist Address For Split Amount)
        uint _StartingTimestamp;   // [4] -> _StartingTimestamp (Unix Start Time)
        uint _SecondsPerTokenID;   // [5] -> _SecondsPerTokenID (600 = 10 Minutes For Each TokenID)
        uint _SecondsIntermission; // [6] -> _SecondsIntermission (eg. If You Want To Close Draft At Midnight And Resume At Noon _SecondsIntermission = 43200 Seconds)
        uint _MaxPerIntermission;  // [7] -> _MaxPerIntermission (Maximum TokenIDs To Claim Per _SecondsIntermission)
        uint _MaxTotalClaim;       // [8] -> _MaxTotalClaim (Maximum Total TokenIDs Available To Claim)
        uint _AvailableDirects;    // [9] -> _AvailableDirects (Amount Of Direct Claims Available To Claim)
        uint _AvailableMintPass;   // [10] -> _AvailableMintPass (Amount Of MintPasses Available To Claim)
        uint _PricePerDirect;      // [11] -> _Price (In Wei To Claim Tokens) Per Unit (Direct Mint)
        uint _PricePerMintPass;    // [12] -> _Price (In Wei To Claim Tokens) Per Unit (Mint Pass Burn)
        uint _BMSplitAmount;       // [13] -> _BMSplitAmount (Amount Of Ether To Contract Owner) (375 = 37.5%)
        uint _ABSplitAmount;       // [14] -> _ABSplitAmount (Amount Of Ether To ArtBlocks) (25 = 2.5%)
        uint _TokenIDStart;        // [15] -> _TokenIDStart (Starting TokenID)
        uint _TokenIDRange;        // [16] -> _TokenIDRange (How Many Tokens From TokenIDStart To Allow Claiming)
        bool _TimelockActive;      // [17] -> _TimelockActive (If True, Claims Are Only Available After Timelock)
        bool _DraftPickActive;     // [18] -> _DraftPickActive (If True, Claims Are Available)
    }

    struct UserClaim
    {
        address _DraftPicker; // [0] -> _DraftPicker
        uint _ClaimedTokenID; // [1] -> _ClaimedTokenID
        uint _BurnedTokenID;  // [2] -> _BurnedTokenID
        uint _Timestamp;      // [3] -> _Timestamp
    }

    uint public _TOTAL_UNIQUE_DRAFT_PICKS = 2;
    address public _BRT_MULTISIG = 0x0BC56e3c1397e4570069e89C07936A5c6020e3BE;
    address public _ART_BLOCKS = 0x43A7D26a271F5801b8092d94DFd5b36EA5D01F5f;

    mapping(uint=>DraftPick) public _DraftPicks;
    mapping(uint=>UserClaim[]) private _UserClaims;
    mapping(uint=>mapping(uint=>bool)) public TokenIDClaimed;
    mapping(address=>bool) public Admin;
    mapping(uint=>uint) public AmountClaimed;
    mapping(uint=>uint) public AmountClaimedDirect;
    mapping(uint=>uint) public AmountClaimedMintPass;

    event Claimed(uint DraftIndex, address Redeemer, uint ClaimedTokenID, uint Timestamp);
    event Purchased(address Purchaser, uint[] Tokens, uint Amount);
    event DraftPickStarted(uint DraftIndex);

    constructor()
    {
        _DraftPicks[0] = DraftPick( 
            "Ana Maria",                                // Name
            0x7B807B46Ee5B65dD20848EE6c805C4A86d06d3Ce, // MintPass
            0x7c3Ea2b7B3beFA1115aB51c09F0C9f245C500B18, // MintedWork
            0x5168D59eFEBF8B86513b8728EC3e2D9Aa099BD93, // Operator
            0xe5BBA0D5628946f1CAB23FdE1496732050435429, // Artist Address
            block.timestamp,                            // Starting Timestamp
            60,                                         // Seconds Per TokenID
            0,                                          // Seconds Intermission
            100,                                        // Max Per Intermission 
            67,                                         // Max Total Claim
            16,                                         // Available Directs
            51,                                         // Available MintPass
            0.15 ether,                                 // Price Per Direct Claim
            0,                                          // Price Per Mint Pass Claim
            345,                                        // BM Split Amount
            25,                                         // AB Split Amount
            20000000,                                   // Starting TokenID
            100,                                        // TokenID Range
            false,                                      // Timelock Active
            false                                       // Draft Pick Claim Active
        );

        _DraftPicks[1] = DraftPick( 
            "Apophenies",                               // Name
            0x4c909BfAFAe145bf53892136A056FB4B36B450b0, // MintPass
            0x0A1BBD57033F57E7B6743621b79fCB9Eb2CE3676, // MintedWork
            0x5168D59eFEBF8B86513b8728EC3e2D9Aa099BD93, // Operator
            0x0BC56e3c1397e4570069e89C07936A5c6020e3BE, // Artist Address
            block.timestamp,                            // Starting Timestamp
            60,                                         // Seconds Per TokenID
            0,                                          // Seconds Intermission
            100,                                        // Max Per Intermission 
            70,                                         // Max Total Claim
            64,                                         // Available Directs
            6,                                          // Available MintPass
            1.5 ether,                                  // Price Per Direct Claim
            0,                                          // Price Per Mint Pass Claim
            1000,                                       // BM Split Amount
            0,                                          // AB Split Amount
            61000000,                                   // Starting TokenID
            100,                                        // TokenID Range
            false,                                      // Timelock Active
            false                                       // Draft Pick Claim Active
        );

        Admin[msg.sender] = true; 
        Admin[0xe06F5FAE754e81Bc050215fF89B03d9e9FF20700] = true;
        Admin[0x90D98d5A1fD21B7cEa4D5c18341607ed1a8345c0] = true;
        Admin[0x18B7511938FBe2EE08ADf3d4A24edB00A5C9B783] = true;
    }

    /**
     * @dev Purchases Tokens In Draft Pick Directly, With An Optional Price
     * @param DraftIndex The Draft Index To Claim From
     * @param RedeemTokenIDs The TokenIDs To Redeem
     */
    function PurchaseTokensDirect(
        uint DraftIndex, 
        uint[] calldata RedeemTokenIDs
    ) external payable nonReentrant {
        require(_DraftPicks[DraftIndex]._DraftPickActive, "DraftPicker: Draft Pick Is Not Active");
        require(block.timestamp >= _DraftPicks[DraftIndex]._StartingTimestamp, "DraftPicker: Claiming Has Not Started Yet");
        require(msg.value == RedeemTokenIDs.length * _DraftPicks[DraftIndex]._PricePerDirect, "DraftPicker: Invalid Amount Of Ether Sent");
        require(AmountClaimed[DraftIndex] + RedeemTokenIDs.length <= _DraftPicks[DraftIndex]._MaxTotalClaim, "DraftPicker: Max Claim Reached");
        require(AmountClaimedDirect[DraftIndex] + RedeemTokenIDs.length <= _DraftPicks[DraftIndex]._AvailableDirects, "DraftPicker: Max Direct Claim Reached");
        require(!_DraftPicks[DraftIndex]._TimelockActive, "DraftPicker: Timelock Is Active");
        AmountClaimedDirect[DraftIndex] += RedeemTokenIDs.length;
        AmountClaimed[DraftIndex] += RedeemTokenIDs.length;
        uint Start = _DraftPicks[DraftIndex]._TokenIDStart;
        uint End = Start + _DraftPicks[DraftIndex]._TokenIDRange;
        __FinalizeDraftPickSale(DraftIndex, msg.value);
        for(uint x; x < RedeemTokenIDs.length; x++)
        {
            require(RedeemTokenIDs[x] >= Start && RedeemTokenIDs[x] <= End, "DraftPicker: Invalid Claim TokenID");
            require(!TokenIDClaimed[DraftIndex][RedeemTokenIDs[x]], "DraftPicker: Desired TokenID Already Claimed");
            TokenIDClaimed[DraftIndex][RedeemTokenIDs[x]] = true;
            IERC721(_DraftPicks[DraftIndex]._MintedWork).transferFrom(_DraftPicks[DraftIndex]._Operator, msg.sender, RedeemTokenIDs[x]);
            _UserClaims[DraftIndex].push(UserClaim(msg.sender, RedeemTokenIDs[x], type(uint).max, block.timestamp));
            emit Claimed(DraftIndex, msg.sender, RedeemTokenIDs[x], block.timestamp);
        }
    }

    /**
     * @dev Claims And Optionally Purchases Tokens In Draft Pick With Token Range Of Eligible Tokens
     * @param DraftIndex The Draft Index To Claim From
     * @param BurnTokenIDs The TokenIDs To Burn
     * @param RedeemTokenIDs The TokenIDs To Redeem
     */
    function ClaimTokensMintPassBurnWithTokenRange(
        uint DraftIndex, 
        uint[] calldata BurnTokenIDs, 
        uint[] calldata RedeemTokenIDs
    ) external payable nonReentrant {
        require(_DraftPicks[DraftIndex]._DraftPickActive, "DraftPicker: Draft Pick Is Not Active");
        require(block.timestamp >= _DraftPicks[DraftIndex]._StartingTimestamp, "DraftPicker: Claiming Has Not Started Yet");
        require(msg.value == BurnTokenIDs.length * _DraftPicks[DraftIndex]._PricePerMintPass, "DraftPicker: Invalid Amount Of Ether Sent");
        require(BurnTokenIDs.length == RedeemTokenIDs.length, "DraftPicker: Invalid Array Input Length");
        require(AmountClaimed[DraftIndex] + BurnTokenIDs.length <= _DraftPicks[DraftIndex]._MaxTotalClaim, "DraftPicker: Max Claim Reached");
        require(AmountClaimedMintPass[DraftIndex] + BurnTokenIDs.length <= _DraftPicks[DraftIndex]._AvailableMintPass, "DraftPicker: Max MintPass Claim Reached");
        require(!_DraftPicks[DraftIndex]._TimelockActive, "DraftPicker: Timelock Is Active");
        AmountClaimedMintPass[DraftIndex] += BurnTokenIDs.length;
        AmountClaimed[DraftIndex] += BurnTokenIDs.length;
        uint Start = _DraftPicks[DraftIndex]._TokenIDStart;
        uint End = Start + _DraftPicks[DraftIndex]._TokenIDRange;
        __FinalizeDraftPickSale(DraftIndex, msg.value);
        for(uint x; x < BurnTokenIDs.length; x++)
        {            
            require(RedeemTokenIDs[x] >= Start && RedeemTokenIDs[x] <= End, "DraftPicker: Invalid Claim TokenID");
            require(!TokenIDClaimed[DraftIndex][RedeemTokenIDs[x]], "DraftPicker: Desired TokenID Already Claimed");
            TokenIDClaimed[DraftIndex][RedeemTokenIDs[x]] = true;
            (address Recipient, ) = IMP(_DraftPicks[DraftIndex]._MintPass)._LiveMintBurn(BurnTokenIDs[x]);
            require(Recipient == msg.sender, "DraftPicker: Msg.Sender Is Not Owner Of Input Mint Pass");
            IERC721(_DraftPicks[DraftIndex]._MintedWork).transferFrom(_DraftPicks[DraftIndex]._Operator, Recipient, RedeemTokenIDs[x]);
            _UserClaims[DraftIndex].push(UserClaim(msg.sender, RedeemTokenIDs[x], BurnTokenIDs[x], block.timestamp));
            emit Claimed(DraftIndex, msg.sender, RedeemTokenIDs[x], block.timestamp);
        }
    }

    /**
     * @dev Claims And Optionally Purchases Tokens In Draft Pick With Mint Pass Timelock
     * @param DraftIndex The Draft Index To Claim From
     * @param BurnTokenIDs The TokenIDs To Burn
     * @param RedeemTokenIDs The TokenIDs To Redeem
     */
    function ClaimTokensMintPassBurnWithTimelock(
        uint DraftIndex, 
        uint[] calldata BurnTokenIDs, 
        uint[] calldata RedeemTokenIDs
    ) external payable nonReentrant {
        require(_DraftPicks[DraftIndex]._DraftPickActive, "DraftPicker: Draft Pick Is Not Active");
        require(block.timestamp >= _DraftPicks[DraftIndex]._StartingTimestamp, "DraftPicker: Claiming Has Not Started Yet");
        require(msg.value == RedeemTokenIDs.length * _DraftPicks[DraftIndex]._PricePerMintPass, "DraftPicker: Invalid Amount Of Ether Sent");
        require(BurnTokenIDs.length == RedeemTokenIDs.length, "DraftPicker: Invalid Array Input Length");
        require(AmountClaimed[DraftIndex] + BurnTokenIDs.length <= _DraftPicks[DraftIndex]._MaxTotalClaim, "DraftPicker: Max Claim Reached");
        require(AmountClaimedMintPass[DraftIndex] + BurnTokenIDs.length <= _DraftPicks[DraftIndex]._AvailableMintPass, "DraftPicker: Max MintPass Claim Reached");
        require(_DraftPicks[DraftIndex]._TimelockActive, "DraftPicker: Timelock Is Not Active");
        AmountClaimedMintPass[DraftIndex] += BurnTokenIDs.length;
        AmountClaimed[DraftIndex] += BurnTokenIDs.length;
        uint ValidBurnTokenID = ViewValidTokenID(DraftIndex, block.timestamp);
        uint Start = _DraftPicks[DraftIndex]._TokenIDStart;
        uint End = Start + _DraftPicks[DraftIndex]._TokenIDRange;
        __FinalizeDraftPickSale(DraftIndex, msg.value);
        for(uint x; x < BurnTokenIDs.length; x++)
        {
            require(RedeemTokenIDs[x] >= Start && RedeemTokenIDs[x] <= End, "DraftPicker: Invalid Claim TokenID");
            require(BurnTokenIDs[x] <= ValidBurnTokenID, "DraftPicker: MintPass TokenID Not Yet Burnable");
            require(!TokenIDClaimed[DraftIndex][RedeemTokenIDs[x]], "DraftPicker: Desired TokenID Already Claimed");
            TokenIDClaimed[DraftIndex][RedeemTokenIDs[x]] = true;
            (address Recipient, ) = IMP(_DraftPicks[DraftIndex]._MintPass)._LiveMintBurn(BurnTokenIDs[x]);
            require(Recipient == msg.sender, "DraftPicker: Msg.Sender Is Not Owner Of Input Mint Pass");
            IERC721(_DraftPicks[DraftIndex]._MintedWork).transferFrom(_DraftPicks[DraftIndex]._Operator, Recipient, RedeemTokenIDs[x]);
            _UserClaims[DraftIndex].push(UserClaim(msg.sender, RedeemTokenIDs[x], BurnTokenIDs[x], block.timestamp));
            emit Claimed(DraftIndex, msg.sender, RedeemTokenIDs[x], block.timestamp);
        }
    }   

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

    /**
     * @dev Starts A Draft Pick Claim (DP = Draft Pick) 
     */
    function _StartDraftPickClaim(DraftPick memory _DP) external onlyAdmin returns (uint DraftPickIndex)
    {
        DraftPickIndex = _TOTAL_UNIQUE_DRAFT_PICKS;
        require(_DP._TokenIDRange <= 1000000, "DraftPicker: Invalid TokenID Range | Maximum = 1,000,000");
        _DraftPicks[DraftPickIndex] = _DP;
        emit DraftPickStarted(DraftPickIndex);
    }

    /**
     * @dev Changes The Name Of A Draft Pick
     */
    function _ChangeName(uint DraftIndex, string calldata Name) external onlyAdmin { _DraftPicks[DraftIndex]._Name = Name; }

    /**
     * @dev Changes All Addresses
     */
    function _ChangeAddresses(uint DraftIndex, address NewMintPass, address NewMintedWork, address Operator, address ArtistAddress) external onlyAdmin
    {
        _DraftPicks[DraftIndex]._MintPass = NewMintPass;
        _DraftPicks[DraftIndex]._MintedWork = NewMintedWork;
        _DraftPicks[DraftIndex]._Operator = Operator;
        _DraftPicks[DraftIndex]._ArtistAddress = ArtistAddress;
    }

    /**
     * @dev Changes The Current Mint Pass Address
     */
    function _ChangeAddressMintPass(uint DraftIndex, address NewAddress) external onlyAdmin { _DraftPicks[DraftIndex]._MintPass = NewAddress; }

    /**
     * @dev Changes The Current Minted Work Address
     */
    function _ChangeAddressMintedWork(uint DraftIndex, address NewAddress) external onlyAdmin { _DraftPicks[DraftIndex]._MintedWork = NewAddress; }

    /**
     * @dev Changes The Operator Address
     */
    function _ChangeAddressOperator(uint DraftIndex, address NewAddress) external onlyAdmin { _DraftPicks[DraftIndex]._Operator = NewAddress; }

    /**
     * @dev Changes The Artist Address
     */
    function _ChangeAddressArtist(uint DraftIndex, address NewAddress) external onlyAdmin { _DraftPicks[DraftIndex]._ArtistAddress = NewAddress; }

    /**
     * @dev Changes Unix Start Time & The Seconds Interval
     */
    function _ChangeSecondsAndStart(uint DraftIndex, uint StartingTimestamp, uint SecondsInterval) external onlyAdmin
    {
        _DraftPicks[DraftIndex]._StartingTimestamp = StartingTimestamp;
        _DraftPicks[DraftIndex]._SecondsPerTokenID = SecondsInterval;
    }

    /**
     * @dev Changes The Starting Timestamp For Timelocked Draft Pick Merkle Claims
     */
    function _ChangeStartingTimestamp(uint DraftIndex, uint UnixTimestamp) external onlyAdmin { _DraftPicks[DraftIndex]._StartingTimestamp = UnixTimestamp; }

    /**
     * @dev Changes The Seconds Per TokenID Claim (600 = 10 minutes Per TokenID)
     */
    function _ChangeSecondsPerTokenID(uint DraftIndex, uint Seconds) external onlyAdmin { _DraftPicks[DraftIndex]._SecondsPerTokenID = Seconds; }

    /**
     * @dev Changes The Amount Of Seconds In Each Intermission
     */
    function _ChangeSecondsIntermission(uint DraftIndex, uint Seconds) external onlyAdmin { _DraftPicks[DraftIndex]._SecondsIntermission = Seconds; }

    /**
     * @dev Changes The Maximum TokenIDs Available Per Intermission Interval
     */
    function _ChangeMaxPerIntermission(uint DraftIndex, uint Max) external onlyAdmin { _DraftPicks[DraftIndex]._MaxPerIntermission = Max; }
    
    /**
     * @dev Changes The Max Claimable Tokens For Timelocked Draft Pick Merkle Claims
     */
    function _ChangeMaxClaim(uint DraftIndex, uint Max) external onlyAdmin { _DraftPicks[DraftIndex]._MaxTotalClaim = Max; }

    /**
     * @dev Changes The Amount Of Direct Claims Available For Draft Pick Claims
     */
    function _ChangeAvailableDirects(uint DraftIndex, uint Amount) external onlyAdmin { _DraftPicks[DraftIndex]._AvailableDirects = Amount; }

    /**
     * @dev Changes The Amount Of MintPasses Available For Draft Pick Claims
     */
    function _ChangeAvailableMintPass(uint DraftIndex, uint Amount) external onlyAdmin { _DraftPicks[DraftIndex]._AvailableMintPass = Amount; }

    /**
     * @dev Changes The Price In Wei To Claim Tokens
     */
    function _ChangePricePerDirect(uint DraftIndex, uint Price) external onlyAdmin { _DraftPicks[DraftIndex]._PricePerDirect = Price; }

    /**
     * @dev Changes The Price In Wei To Claim Tokens
     */
    function _ChangePricePerMintPass(uint DraftIndex, uint Price) external onlyAdmin { _DraftPicks[DraftIndex]._PricePerMintPass = Price; }

    /**
     * @dev Changes The Split Amount For BM Recipient Split
     */
    function _ChangeSplitAmountBM(uint DraftIndex, uint SplitAmount) external onlyAdmin { _DraftPicks[DraftIndex]._BMSplitAmount = SplitAmount; }

    /**
     * @dev Changes The Split Amount For AB Recipient Split
     */
    function _ChangeSplitAmountAB(uint DraftIndex, uint SplitAmount) external onlyAdmin { _DraftPicks[DraftIndex]._ABSplitAmount = SplitAmount; }

    /**
     * @dev Changes The Starting TokenID
     */
    function _ChangeTokenIDStart(uint DraftIndex, uint TokenID) external onlyAdmin { _DraftPicks[DraftIndex]._TokenIDStart = TokenID; }

    /**
     * @dev Changes The TokenID Range
     */
    function _ChangeTokenIDRange(uint DraftIndex, uint Range) external onlyAdmin { _DraftPicks[DraftIndex]._TokenIDRange = Range; }

    /**
     * @dev Changes The Active State Of The Draft Pick
     */
    function _ChangeActiveState(uint DraftIndex, bool ActiveState) external onlyAdmin { _DraftPicks[DraftIndex]._DraftPickActive = ActiveState; }

    /**
     * @dev Withdraws All ETH To Multisig
     */
    function _WithdrawETHToMultisig() external onlyAdmin 
    { 
        (bool Success,) = _BRT_MULTISIG.call{value:(address(this).balance)}("");
        require(Success, "i have failed u anakin");
    }

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

    /**
     * @dev Changes The BrightMoments Split Address
     */
    function ____ChangeSplitAddressBrightMoments(address NewAddress) external onlyOwner { _BRT_MULTISIG = NewAddress; }

    /**
     * @dev Changes The ArtBlocks Address
     */
    function ____ChangeSplitAddressArtBlocks(address NewAddress) external onlyOwner { _ART_BLOCKS = NewAddress; }

    /**
     * @dev Executes Arbitrary Transaction(s)
     */
    function ____InitTransactions(address[] memory Targets, uint[] memory Values, bytes[] memory Datas) external onlyOwner
    {
        for(uint x; x < Targets.length; x++) 
        {
            (bool success,) = Targets[x].call{value:(Values[x])}(Datas[x]);
            require(success, "i have failed u anakin");
        }
    }

    /**
     * @dev Adds An Admin
     */
    function ____AddressAuthorize(address Wallet) external onlyOwner { Admin[Wallet] = true; }

    /**
     * @dev Removes An Admin
     */
    function ____AddressDeauthorize(address Wallet) external onlyOwner { Admin[Wallet] = false; }

    /**
     * @dev Withdraws All ETH
     */
    function ____WithdrawETHPayable() external onlyOwner { payable(msg.sender).transfer(address(this).balance); }

    /**
     * @dev Withdraws All ETH
     */
    function ____WithdrawETHCall() external onlyOwner 
    { 
        (bool success,) = msg.sender.call{value:(address(this).balance)}("");
        require(success, "i have failed u anakin");
    }

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

    /**
     * @dev Returns The Current Eligible TokenID For Timelocked Draft Pick Merkle Claims
     */
    function ViewFrontend(uint DraftIndex) public view returns ( 
        UserClaim[] memory AllClaims, 
        DraftPick memory ClaimParams, 
        uint[] memory Timetable, 
        uint ValidTokenID, 
        uint ClaimedAmount 
    ) {
        ValidTokenID = ViewValidTokenID(DraftIndex, block.timestamp);
        return (
            _UserClaims[DraftIndex], 
            _DraftPicks[DraftIndex], 
            ViewTimetable(DraftIndex), 
            ValidTokenID, 
            AmountClaimed[DraftIndex]
        );
    }

    /**
     * @dev Returns Multiple Draft Pick Frontends
     */
    function ViewFrontends(uint[] calldata DraftIndexes) public view returns ( 
        UserClaim[][] memory AllClaims, 
        DraftPick[] memory ClaimParams, 
        uint[][] memory Timetable, 
        uint[] memory ValidTokenIDs, 
        uint[] memory ClaimedAmounts 
    ) {
        AllClaims = new UserClaim[][](DraftIndexes.length);
        ClaimParams = new DraftPick[](DraftIndexes.length);
        Timetable = new uint[][](DraftIndexes.length);
        ValidTokenIDs = new uint[](DraftIndexes.length);
        ClaimedAmounts = new uint[](DraftIndexes.length);
        for(uint x; x < DraftIndexes.length; x++)
        {
            if(_DraftPicks[DraftIndexes[x]]._TimelockActive)
            {
                ValidTokenIDs[x] = ViewValidTokenID(DraftIndexes[x], block.timestamp);
                Timetable[x] = ViewTimetable(DraftIndexes[x]);
            }
            AllClaims[x] = _UserClaims[DraftIndexes[x]];
            ClaimParams[x] = _DraftPicks[DraftIndexes[x]];
            ClaimedAmounts[x] = AmountClaimed[DraftIndexes[x]];
        }
        return (AllClaims, ClaimParams, Timetable, ValidTokenIDs, ClaimedAmounts);
    }
    
    /**
     * @dev Returns An Array Of Successful Claims
     */
    function ViewClaims(uint DraftIndex) public view returns ( UserClaim[] memory AllClaims ) { return _UserClaims[DraftIndex]; }

    /**
     * @dev Returns The Timetable Of Unix Timestamps For Given Range Of TokenIDs
     */
    function ViewTimetable(uint DraftIndex) public view returns ( uint[] memory Timestamps ) 
    {
        Timestamps = new uint[](_DraftPicks[DraftIndex]._MaxTotalClaim);
        for (uint x; x < _DraftPicks[DraftIndex]._MaxTotalClaim; x++) { Timestamps[x] = ViewTimestampForTokenID(DraftIndex, x); }
        return Timestamps;
    }

    /**
     * @dev Calculates the Unix Timestamp for a given TokenID
     */
    function ViewTimestampForTokenID(uint DraftIndex, uint TokenID) public view returns ( uint ) 
    {
        uint MaxPerLocalInterval = _DraftPicks[DraftIndex]._MaxPerIntermission;
        uint SecondsIntermission = _DraftPicks[DraftIndex]._SecondsIntermission;
        uint Interval = _DraftPicks[DraftIndex]._SecondsPerTokenID;
        uint FullIntervals = TokenID / MaxPerLocalInterval;
        uint CurrentIntervalIncrements = TokenID % MaxPerLocalInterval;
        return _DraftPicks[DraftIndex]._StartingTimestamp + (FullIntervals * SecondsIntermission) + (CurrentIntervalIncrements * Interval);
    }

    /**
     * @dev Returns The Current Valid TokenID
     */
    function ViewValidTokenID ( uint DraftIndex, uint UnixTimestamp ) public view returns ( uint )
    { 
        if(UnixTimestamp < _DraftPicks[DraftIndex]._StartingTimestamp) { return type(uint).max; }
        uint MaxPerLocalInterval = _DraftPicks[DraftIndex]._MaxPerIntermission;
        uint SecondsIntermission = _DraftPicks[DraftIndex]._SecondsIntermission;
        uint ElapsedTime;
        uint CurrentIntervalIncrements;
        if(SecondsIntermission == 0)
        {
            ElapsedTime = UnixTimestamp - _DraftPicks[DraftIndex]._StartingTimestamp;
            CurrentIntervalIncrements = ElapsedTime / _DraftPicks[DraftIndex]._SecondsPerTokenID;
            if(CurrentIntervalIncrements >= MaxPerLocalInterval) { CurrentIntervalIncrements = MaxPerLocalInterval - 1; }
            return CurrentIntervalIncrements;
        }
        else
        {
            ElapsedTime = UnixTimestamp - _DraftPicks[DraftIndex]._StartingTimestamp;
            uint FullIntervals = (ElapsedTime / SecondsIntermission);
            uint HalfLivesCompleted = ElapsedTime % SecondsIntermission;
            CurrentIntervalIncrements = HalfLivesCompleted / _DraftPicks[DraftIndex]._SecondsPerTokenID;
            if(CurrentIntervalIncrements >= MaxPerLocalInterval) { CurrentIntervalIncrements = MaxPerLocalInterval - 1; }
            return (FullIntervals * MaxPerLocalInterval) + CurrentIntervalIncrements;
        }
    }

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

    /**
     * @dev Finalizes ETH Transfers 
     */
    function __FinalizeDraftPickSale(uint DraftIndex, uint MessageValue) internal
    {
        if(MessageValue > 0)
        {
            require(_DraftPicks[DraftIndex]._ArtistAddress != address(0), "DraftPicker: Artist Address Not Set");
            uint BrightMomentsSplit = MessageValue * _DraftPicks[DraftIndex]._BMSplitAmount / 1000; 
            uint ArtBlocksSplit = MessageValue * _DraftPicks[DraftIndex]._ABSplitAmount / 1000; 
            uint ArtistAmount = MessageValue - BrightMomentsSplit - ArtBlocksSplit;
            (bool Tx1,) = _BRT_MULTISIG.call { value: BrightMomentsSplit }("");
            (bool Tx2,) = _ART_BLOCKS.call { value: ArtBlocksSplit }("");
            (bool Tx3,) = _DraftPicks[DraftIndex]._ArtistAddress.call { value: ArtistAmount }("");
            require(Tx1 && Tx2 && Tx3, "DraftPicker: ETH Transfers Failed");
        }
    }

    /**
     * @dev onlyAdmin Access Modifier
     */
    modifier onlyAdmin
    {
        require(Admin[msg.sender], "DraftPicker | onlyAdmin | Caller Is Not Admin");
        _;
    }
}

interface IERC721 { function transferFrom(address from, address to, uint256 tokenId) external; }
// File: contracts/IMP.sol



// SPDX-License-Identifier: MIT
/**
 * @dev @brougkr
 */
pragma solidity 0.8.19;
interface IMP 
{ 
    /**
     * @dev { For Instances Where Golden Token Or Artists Have A Bespoke Mint Pass Contract }
     */
    function _LiveMintBurn(uint TicketID) external returns (address Recipient, uint ArtistID); 
}