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



// SPDX-License-Identifier: MIT

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() {
        _setOwner(_msgSender());
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(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");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

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



// SPDX-License-Identifier: MIT

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 make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, 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 be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

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

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

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

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

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



// SPDX-License-Identifier: MIT

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/introspection/IERC165.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

// File: @openzeppelin/contracts/utils/structs/EnumerableSet.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastvalue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

// File: contracts/EarlyBirdRegistry/IEarlyBirdRegistry.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title IEarlyBirdRegistry
/// @author Simon Fremaux (@dievardump)
interface IEarlyBirdRegistry {
    /// @notice allows anyone to register a new project that accepts Early Birds registrations
    /// @param open if the early bird registration is open or only creator can register addresses
    /// @param endRegistration unix epoch timestamp of registration closing
    /// @param maxRegistration the max registration count
    /// @return projectId the project Id (useful if called by a contract)
    function registerProject(
        bool open,
        uint256 endRegistration,
        uint256 maxRegistration
    ) external returns (uint256 projectId);

    /// @notice tells if a project exists
    /// @param projectId project id to check
    /// @return if the project exists
    function exists(uint256 projectId) external view returns (bool);

    /// @notice Helper to paginate all address registered for a project
    /// @param projectId the project id
    /// @param offset index where to start
    /// @param limit how many to grab
    /// @return list of registered addresses
    function listRegistrations(
        uint256 projectId,
        uint256 offset,
        uint256 limit
    ) external view returns (address[] memory list);

    /// @notice Helper to know how many address registered to a project
    /// @param projectId the project id
    /// @return how many people registered
    function registeredCount(uint256 projectId) external view returns (uint256);

    /// @notice Helper to check if an address is registered for a project id
    /// @param check the address to check
    /// @param projectId the project id
    /// @return if the address was registered as an early bird
    function isRegistered(address check, uint256 projectId)
        external
        view
        returns (bool);

    /// @notice Allows a project creator to add early birds in Batch
    /// @dev msg.sender must be the projectId creator
    /// @param projectId to add to
    /// @param birds all addresses to add
    function registerBatchTo(uint256 projectId, address[] memory birds)
        external;
}

// File: contracts/Sower.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/@%,,,,,,,@#,@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&/%,,**,***,*,,,*(#@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*%*,,**********,,/%%@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@*,************,/@(@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(@(,**,********,/@/@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@,%,,**********,,#*@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.%,,*********,*&(@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&*(#(,,******,,,,,&/@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&,&@#,,,*****,***,*%,@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&,(***,******************,%&&/&@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@*,**************************,/&(#@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@,%*******************************,*,#,/@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@#&***************************************#%*#@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@*&*******************************************/%(%%%((@
// @@@@@@@@@@@@@@@@@@@@@@@@ %*****************************************************#
// @@@@@@@@@@@@@@@@@@@@@&(%*************************************/&@(***************
// @@@@@@@@@@@@@@@@@@@#%%/***********%(#************************(//(((************#
// @@@@@@@@@@@@@@@@@@/(************@#&&#/***************************************//%
// @@@@@@@@@@@@@@@@#@**********&###@@@@&#&*************************************#/@@
// @@@@@@@@@@@@@@/%**********,%%@@@@@@@@*&************************************/#&@@
// @@@@@@@@@@@@%#/********/(#@@@@@@@@@@@@@(@%********************************&,@@@@
// @@@@@%#%####*********#(#@@@@@@@@@@@@@@@#@(******************************%&#@@@@@
// @@(@%/************@#@@@@@@@@@@@@@@@@@@@%@********************&*******@%,@@@@@@@@
// @/&//*////////*(@/@@@@@@@@@@@@@@@@@@@@&#/*////////////////**@%%(///%@@@@@@@@@@@@
// %(&/////////**%(&@@@@@@@@@@@@@@@@@@@@@(#//////////////////*/@*@@@@@@@@@@@@@@@@@@
// @@@*@@*//*#@&#@@@@@@@@@@@@@@@@@@@@@@@*@#*/////////////////*%@/@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/#///////////////////%(%@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&/%///////////////////@##@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/#///////////////////(@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/#///////////////////%,@@@@@@@@@@@@@@@@
// @@@@@,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(&#////////////////////@/@@@@@@@@@@@@@@@
// @@@,@@@@@@@,@@@@@@@@@@@@@@@@@@@@@@@@@@@@*&//////////////////////&%@@@@@@@@@@@@@@
// @@@@,@@@,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%#&//////////////////////(&(@@@@@@@@@@@@@@
// @@@@@@@@@,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(@*///////////////////////%/#@@@@@@@@@@
// @@@@@@,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@(/////////////////////////&&&@@@@@@@@@
// @@@,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*@(////////////%%/////////////%(@@@@@@@@@
// @@@@@,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&#(/////////////&/@#////////////(&*@@@@@@@@
// @@@@@@@,@@@@@@@@@@@@@@@@@@@@@@@@@@@@(@(((((((((((//#%&@#&/((((((((((((&*@@@@@@@@
// @@@@,@@@@,@@@@@@@@@@@@@@@@@@@@@@@@@%@((((((((((((#(%@@@@(@((((((((((((&/@@@@@@@@
// @@,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##((((((((((((&&@@@@#&@((((((((((((&,@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@(@%(((((((((((&/@@@@@@@(@((((((((((((&@&@@@@@@@
// @@@@@,@@@@@@@@@@@@@@@@@@@@@@@@@@@%(#(#########(%%@@@@@@@@@#&(#########(&@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%###########@%%@@@@@@@@@/@%##########&/@@@@@@@@
// @@,@@@@@@@@@@@@@@@@@@@@@@@@@@@@/&##########%&#@@@@@@@@@@@/@%###########@#@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@&&%%%%%%%%%%%@#@@@@@@@@@@@@@&%%%%%%%%%%%%@@#@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@&%&%%%%%%%%%%&&#@@@@@@@@@@@@@@&@%%%%%%%%%%%@%@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@/@%%%%%%%%%%%%@#@@@@@@@@@@@@@@%@%%%%%%%%%%%&%@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@/@%%%%%%%%%%%%%&@/%@@@@@@@@@@@@@%%%%%%%%%%%%%@#@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&&&@@%&%%%%%%%%%%%@@&@@@@@@@@@%%%%%%%%%%%%%%%&@@#(
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&&&%%%%%%%%%%&&%@@@@@@&@%%%%%%%%%%%%%%%%%%%%
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##%#@@@@#@@@@@@@@@@@@@@@@@@@#%%%%%%%%%%%

import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import '@openzeppelin/contracts/access/Ownable.sol';

import './Variety/IVariety.sol';
import './EarlyBirdRegistry/IEarlyBirdRegistry.sol';

/// @title Sower
/// @author Simon Fremaux (@dievardump)
contract Sower is Ownable, ReentrancyGuard {
    using EnumerableSet for EnumerableSet.AddressSet;
    using EnumerableSet for EnumerableSet.UintSet;

    event Collected(
        address indexed operator,
        address indexed variety,
        uint256 indexed count,
        uint256 value
    );

    event EarlyBirdSessionAdded(uint256 sessionId, uint256 projectId);
    event EarlyBirdSessionRemoved(uint256 sessionId, uint256 projectId);

    event VarietyAdded(address variety);
    event VarietyChanged(address variety);
    event VarietyEmpty(address variety);

    event DonationRecipientAdded(address recipient);
    event DonationRecipientRemoved(address recipient);

    struct VarietyData {
        uint8 maxPerCollect; // how many can be collected at once. 0 == no limit
        bool active; // if Variety is active or not
        bool curated; // curated Varieties can only be minted by Variety creator
        address location; // address of the Variety contract
        address creator; // creator of the variety (in case the contract opens to more creators)
        uint256 price; // price of collecting
        uint256 available; // how many are available
        uint256 reserve; // how many are reserve for creator
        uint256 earlyBirdUntil; // earlyBird limit timestamp
        uint256 earlyBirdSessionId; // earlyBirdSessionId
    }

    // main donation, we start with nfDAO
    address public mainDonation = 0x37133cda1941449cde7128f0C964C228F94844a8;

    // Varieties list
    mapping(address => VarietyData) public varieties;

    // list of known varieties address
    EnumerableSet.AddressSet internal knownVarieties;

    // list of address to whom I would like to donate
    EnumerableSet.AddressSet internal donations;

    // last generated seed
    bytes32 public lastSeed;

    // address who used their EarlyBird access
    mapping(uint256 => mapping(address => bool)) internal _earlyBirdsConsumed;

    // the early bird registry
    address public earlyBirdRegistry;

    // because I messed up the EarlyBird registration before the launch
    // I have to use EarlyBirdSession containing one or more EarlyBirgProjectID.
    mapping(uint256 => EnumerableSet.UintSet) internal earlyBirdSessions;

    constructor() {
        // Gitcoin Gnosis
        _addDonationRecipient(0xde21F729137C5Af1b01d73aF1dC21eFfa2B8a0d6);

        // WOCA
        _addDonationRecipient(0xCCa88b952976DA313Fb928111f2D5c390eE0D723);

        // Hardhat deploy / Jolly Roger
        _addDonationRecipient(0xF0D7a8198D75e10517f035CF11b928e9E2aB20f4);
    }

    /// @notice Allows collector to collect up to varietyData.maxPerCollect tokens from variety.
    /// @param count the number of tokens to collect
    /// @param variety the variety to collect from
    function plant(uint256 count, address variety)
        external
        payable
        nonReentrant
    {
        require(count > 0, '!count');

        VarietyData storage varietyData = _getVariety(variety);

        // curated varieties have to be created in a specific way, with the seed, only by creator
        require(varietyData.curated == false, "Can't plant this Variety.");

        // varieties can be paused or out of stock
        require(varietyData.active == true, 'Variety paused or out of seeds.');

        // if we are in an earlyBird phase
        if (varietyData.earlyBirdUntil >= block.timestamp) {
            require(
                isUserInEarlyBirdSession(
                    msg.sender,
                    varietyData.earlyBirdSessionId
                ),
                'Not registered for EarlyBirds'
            );

            require(
                _earlyBirdsConsumed[varietyData.earlyBirdSessionId][
                    msg.sender
                ] == false,
                'Already used your EarlyBird'
            );

            // set early bird as consumed
            _earlyBirdsConsumed[varietyData.earlyBirdSessionId][
                msg.sender
            ] = true;

            require(count == 1, 'Early bird can only grab one');
        }

        require(
            // verifies that there are enough tokens available for this variety
            (varietyData.available - varietyData.reserve) >= count &&
                // and that the user doesn't request more than what is allowed in one tx
                (varietyData.maxPerCollect == 0 ||
                    uint256(varietyData.maxPerCollect) >= count),
            'Too many requested.'
        );

        address operator = msg.sender;

        require(msg.value == varietyData.price * count, 'Value error.');

        _plant(varietyData, count, operator);
    }

    /// @notice Owner function to be able to get varieties from the reserve
    /// @param count how many the owner wants
    /// @param variety from what variety
    /// @param recipient might be a giveaway? recipient can be someone else than owner
    function plantFromReserve(
        uint256 count,
        address variety,
        address recipient
    ) external {
        require(count > 0, '!count');

        VarietyData storage varietyData = _getVariety(variety);

        // curated varieties have to be created in a specific way, with the seed, only by creator
        require(varietyData.curated == false, "Can't plant this Variety.");

        // verify that caller is the variety creator
        // or there is no variety creator and the caller is current owner
        require(
            msg.sender == varietyData.creator ||
                (varietyData.creator == address(0) && msg.sender == owner()),
            'Not Variety creator.'
        );

        require(
            varietyData.reserve >= count && varietyData.available >= count,
            'Not enough reserve.'
        );

        varietyData.reserve -= count;

        if (recipient == address(0)) {
            recipient = msg.sender;
        }

        _plant(varietyData, count, recipient);
    }

    /// @notice Some Varieties can not generate aesthetic output with random seeds.
    ///         Those are "curated Varieties" that only the creator can mint from with curated seeds
    ///         The resulting Seedlings will probably be gifted or sold directly on Marketplaces
    ///         (direct sale or auction)
    /// @param variety the variety to create from
    /// @param recipient the recipient of the creation
    /// @param seeds the seeds to create
    function plantFromCurated(
        address variety,
        address recipient,
        bytes32[] memory seeds
    ) external {
        require(seeds.length > 0, '!count');

        VarietyData storage varietyData = _getVariety(variety);

        // verify this variety is indeed a curated one
        require(varietyData.curated == true, 'Variety not curated.');

        // verify that caller is the variety creator
        // or there is no variety creator and the caller is current owner
        require(
            msg.sender == varietyData.creator ||
                (varietyData.creator == address(0) && msg.sender == owner()),
            'Not Variety creator.'
        );

        if (recipient == address(0)) {
            recipient = msg.sender;
        }

        _plantSeeds(varietyData, recipient, seeds);
    }

    /// @notice Helper to list all Varieties
    /// @return list of varieties
    function listVarieties() external view returns (VarietyData[] memory list) {
        uint256 count = knownVarieties.length();
        list = new VarietyData[](count);
        for (uint256 i; i < count; i++) {
            list[i] = varieties[knownVarieties.at(i)];
        }
    }

    /// @notice Adds a new variety to the list
    /// @param newVariety the variety to be added
    /// @param price the collection cost
    /// @param maxPerCollect how many can be collected at once; 0 == no limit
    /// @param active if the variety is active or not
    /// @param creator variety creator
    /// @param available variety supply
    /// @param reserve variety reserve for variety creator
    /// @param curated if the variety is curated; if yes only creator can mint from it
    function addVariety(
        address newVariety,
        uint256 price,
        uint8 maxPerCollect,
        bool active,
        address creator,
        uint256 available,
        uint256 reserve,
        bool curated
    ) external onlyOwner {
        require(
            !knownVarieties.contains(newVariety),
            'Variety already exists.'
        );
        knownVarieties.add(newVariety);

        varieties[newVariety] = VarietyData({
            maxPerCollect: maxPerCollect,
            price: price,
            active: active,
            creator: creator,
            location: newVariety,
            available: available,
            reserve: reserve,
            curated: curated,
            earlyBirdUntil: 0,
            earlyBirdSessionId: 0
        });

        emit VarietyAdded(newVariety);
    }

    /// @notice Allows to toggle a variety active state
    /// @param variety the variety address
    /// @param isActive if active or not
    function setActive(address variety, bool isActive) public onlyOwner {
        VarietyData storage varietyData = _getVariety(variety);
        require(
            !isActive || varietyData.available > 0,
            "Can't activate empty variety."
        );
        varietyData.active = isActive;
        emit VarietyChanged(variety);
    }

    /// @notice Allows to change the max per collect for a variety
    /// @param variety the variety address
    /// @param maxPerCollect new max per collect
    function setMaxPerCollect(address variety, uint8 maxPerCollect)
        external
        onlyOwner
    {
        VarietyData storage varietyData = _getVariety(variety);
        varietyData.maxPerCollect = maxPerCollect;
        emit VarietyChanged(variety);
    }

    /// @notice activate EarlyBird for a Variety.
    ///         When earlyBird, only registered address can plant
    /// @param varieties_ the varieties address
    /// @param earlyBirdDuration duration of Early Bird from now on
    /// @param earlyBirdSessionId the session id containing projects to check on the EarlyBirdRegistry
    /// @param activateVariety if the variety must be automatically activated (meaning early bird starts now)
    function activateEarlyBird(
        address[] memory varieties_,
        uint256 earlyBirdDuration,
        uint256 earlyBirdSessionId,
        bool activateVariety
    ) external onlyOwner {
        require(
            earlyBirdSessions[earlyBirdSessionId].length() > 0,
            'Session id empty'
        );

        for (uint256 i; i < varieties_.length; i++) {
            VarietyData storage varietyData = _getVariety(varieties_[i]);
            varietyData.earlyBirdUntil = block.timestamp + earlyBirdDuration;
            varietyData.earlyBirdSessionId = earlyBirdSessionId;

            if (activateVariety) {
                setActive(varieties_[i], true);
            } else {
                emit VarietyChanged(varieties_[i]);
            }
        }
    }

    /// @notice sets early bird registry
    /// @param earlyBirdRegistry_ the registry
    function setEarlyBirdRegistry(address earlyBirdRegistry_)
        external
        onlyOwner
    {
        require(earlyBirdRegistry_ != address(0), 'Wrong address.');
        earlyBirdRegistry = earlyBirdRegistry_;
    }

    /// @notice Allows to add an early bird project id to an "early bird session"
    /// @dev an early bird session is a group of early bird registrations projects
    /// @param sessionId the session to add to
    /// @param projectIds the projectIds (containing registration in EarlyBirdRegistry) to add
    function addEarlyBirdProjectToSession(
        uint256 sessionId,
        uint256[] memory projectIds
    ) external onlyOwner {
        require(sessionId > 0, "Session can't be 0");
        for (uint256 i; i < projectIds.length; i++) {
            require(
                IEarlyBirdRegistry(earlyBirdRegistry).exists(projectIds[i]),
                'Unknown early bird project'
            );
            earlyBirdSessions[sessionId].add(projectIds[i]);
            emit EarlyBirdSessionAdded(sessionId, projectIds[i]);
        }
    }

    /// @notice Allows to remove an early bird project id from an "early bird session"
    /// @dev an early bird session is a group of early bird registrations projects
    /// @param sessionId the session to remove from
    /// @param projectIds the projectIds (containing registration in EarlyBirdRegistry) to remove
    function removeEarlyBirdProjectFromSession(
        uint256 sessionId,
        uint256[] memory projectIds
    ) external onlyOwner {
        require(sessionId > 0, "Session can't be 0");

        for (uint256 i; i < projectIds.length; i++) {
            earlyBirdSessions[sessionId].remove(projectIds[i]);
            emit EarlyBirdSessionRemoved(sessionId, projectIds[i]);
        }
    }

    /// @notice Helper to know if a user is in any of the early bird list for current session
    /// @param user the user to test
    /// @param sessionId the session to test for
    /// @return if the user is registered or not
    function isUserInEarlyBirdSession(address user, uint256 sessionId)
        public
        view
        returns (bool)
    {
        // get all earlyBirdIds attached to the earlyBirdSession
        EnumerableSet.UintSet storage session = earlyBirdSessions[sessionId];
        uint256 count = session.length();

        for (uint256 i; i < count; i++) {
            // if the address is registered to any of those projectId
            if (
                IEarlyBirdRegistry(earlyBirdRegistry).isRegistered(
                    user,
                    session.at(i)
                )
            ) {
                return true;
            }
        }

        // else it's not an early bird
        return false;
    }

    /// @notice Helper to list all donation recipients
    /// @return list of donation recipients
    function listDonations() external view returns (address[] memory list) {
        uint256 count = donations.length();
        list = new address[](count);
        for (uint256 i; i < count; i++) {
            list[i] = donations.at(i);
        }
    }

    /// @notice Allows to add a donation recipient
    /// @param recipient the recipient
    function addDonationRecipient(address recipient) external onlyOwner {
        _addDonationRecipient(recipient);
    }

    /// @notice Allows to remove a donation recipient
    /// @param recipient the recipient
    function removeDonationRecipient(address recipient) external onlyOwner {
        _removeDonationRecipient(recipient);
    }

    /// @notice Set mainDonation donation address
    /// @param newMainDonation the new address
    function setNewMainDonation(address newMainDonation) external onlyOwner {
        mainDonation = newMainDonation;
    }

    /// @notice This function allows Sower to answer to a seed change request
    ///         in the event where a seed would produce errors of rendering
    ///         1) this function can only be called by Sower if the token owner
    ///         asked for a new seed (see Variety contract)
    ///         2) this function will only be called if there is a rendering error
    /// @param tokenId the tokenId that needs update
    function updateTokenSeed(address variety, uint256 tokenId)
        external
        onlyOwner
    {
        require(knownVarieties.contains(variety), 'Unknown variety.');
        IVariety(variety).changeSeedAfterRequest(tokenId);
    }

    /// @dev Owner withdraw balance function
    function withdraw() external onlyOwner {
        require(address(this).balance > 0, "I don't think so.");

        uint256 count = donations.length();

        // forces mainDonation and donations to not be empty
        // Code is law.
        require(
            mainDonation != address(0) && count > 0,
            'You have to give in order to get.'
        );

        bool success;

        // 10% of current balance
        uint256 ten = address(this).balance / 10;

        // send 10% to mainDonation address
        (success, ) = mainDonation.call{value: ten}('');
        require(success, '!success');

        // share 10% between all other donation recipients
        uint256 parts = ten / count;
        for (uint256 i; i < count; i++) {
            (success, ) = donations.at(i).call{value: parts}('');
            require(success, '!success');
        }

        // send the rest to sender; use call since it might be a contract someday
        (success, ) = msg.sender.call{value: address(this).balance}('');
        require(success, '!success');
    }

    /// @dev Receive function for royalties
    receive() external payable {}

    /// @dev Internal collection method
    /// @param varietyData the varietyData
    /// @param count how many to collect
    /// @param operator Seedlings recipient
    function _plant(
        VarietyData storage varietyData,
        uint256 count,
        address operator
    ) internal {
        bytes32 seed = lastSeed;
        bytes32[] memory seeds = new bytes32[](count);
        bytes32 blockHash = blockhash(block.number - 1);
        uint256 timestamp = block.timestamp;

        // generate next seeds
        for (uint256 i; i < count; i++) {
            seed = _nextSeed(seed, timestamp, operator, blockHash);
            seeds[i] = seed;
        }

        // saves lastSeed before planting
        lastSeed = seed;

        _plantSeeds(varietyData, operator, seeds);
    }

    /// @dev Allows to plant a list of seeds
    /// @param varietyData the variety data
    /// @param collector the recipient of the Seedling
    /// @param seeds the seeds to plant
    function _plantSeeds(
        VarietyData storage varietyData,
        address collector,
        bytes32[] memory seeds
    ) internal {
        IVariety(varietyData.location).plant(collector, seeds);
        uint256 count = seeds.length;

        varietyData.available -= count;
        if (varietyData.available == 0) {
            varietyData.active = false;
            emit VarietyEmpty(varietyData.location);
        }

        emit Collected(collector, varietyData.location, count, msg.value);

        // if Variety has a creator that is not contract owner, send them the value directly
        if (
            varietyData.creator != address(0) &&
            msg.value > 0 &&
            varietyData.creator != owner()
        ) {
            (bool success, ) = varietyData.creator.call{value: msg.value}('');
            require(success, '!success');
        }
    }

    /// @dev Calculate next seed using a few on chain data
    /// @param currentSeed the current seed
    /// @param timestamp current block timestamp
    /// @param operator current operator
    /// @param blockHash last block hash
    /// @return a new bytes32 seed
    function _nextSeed(
        bytes32 currentSeed,
        uint256 timestamp,
        address operator,
        bytes32 blockHash
    ) internal view returns (bytes32) {
        return
            keccak256(
                abi.encodePacked(
                    currentSeed,
                    timestamp,
                    operator,
                    blockHash,
                    block.coinbase,
                    block.difficulty,
                    tx.gasprice
                )
            );
    }

    /// @notice Returns a variety, throws if does not exist
    /// @param variety the variety to get
    function _getVariety(address variety)
        internal
        view
        returns (VarietyData storage)
    {
        require(knownVarieties.contains(variety), 'Unknown variety.');
        return varieties[variety];
    }

    /// @dev Allows to add a donation recipient to the list of donations
    /// @param recipient the recipient
    function _addDonationRecipient(address recipient) internal {
        donations.add(recipient);
        emit DonationRecipientAdded(recipient);
    }

    /// @dev Allows to remove a donation recipient from the list of donations
    /// @param recipient the recipient
    function _removeDonationRecipient(address recipient) internal {
        donations.remove(recipient);
        emit DonationRecipientRemoved(recipient);
    }
}

// File: contracts/Variety/IVariety.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import '@openzeppelin/contracts/token/ERC721/IERC721.sol';

/// @title IVariety interface
/// @author Simon Fremaux (@dievardump)
interface IVariety is IERC721 {
    /// @notice mint `seeds.length` token(s) to `to` using `seeds`
    /// @param to token recipient
    /// @param seeds each token seed
    function plant(address to, bytes32[] memory seeds)
        external
        returns (uint256);

    /// @notice this function returns the seed associated to a tokenId
    /// @param tokenId to get the seed of
    function getTokenSeed(uint256 tokenId) external view returns (bytes32);

    /// @notice This function allows an owner to ask for a seed update
    ///         this can be needed because although I test the contract as much as possible,
    ///         it might be possible that one token does not render because the seed creates
    ///         error or even "out of gas" computation. That's why this would allow an owner
    ///         in such case, to request for a seed change that will then be triggered by Sower
    /// @param tokenId id to regenerate seed for
    function requestSeedChange(uint256 tokenId) external;

    /// @notice This function allows Sower to answer to a seed change request
    ///         in the event where a seed would produce errors of rendering
    ///         1) this function can only be called by Sower if the token owner
    ///         asked for a new seed
    ///         2) this function will only be called if there is a rendering error
    ///         or, Vitalik Buterin forbid, a duplicate
    /// @param tokenId id to regenerate seed for
    function changeSeedAfterRequest(uint256 tokenId) external;
}