ETH Price: $3,696.23 (-6.00%)

Token

Black Eyed Creatures (BEC)
 

Overview

Max Total Supply

291,708 BEC

Holders

213 (0.00%)

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Filtered by Token Holder
kbrian.eth
Balance
999 BEC

Value
$0.00
0x800d304399ddfbb0a8ef7acc3197dc99f35d8ae6
Loading...
Loading
Loading...
Loading
Loading...
Loading

OVERVIEW

A sci-fi ERC404 NFT gamified collection with real-time generative PFPs breeding.

# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
BECCore

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 35 : BECCore.sol
// contracts/BECCore.sol
// SPDX-License-Identifier: MIT

/**

    /3333333     /33333333     /333333    
   | 33__  33   | 33_____/    /33__  33   
   | 33  \ 33   | 33         | 33  \__/   
   | 3333333    | 33333      | 33         
   | 33__  33   | 33__/      | 33         
   | 33  \ 33   | 33         | 33    33   
   | 3333333/   | 33333333   |  333333/   
   |_______/    |________/    \______/    

 # https://blackeyedcreatures.com

 */

pragma solidity ^0.8.25;

import {ERC404B} from "./erc404/ERC404B.sol";
import {BECURIStorage} from "./BECURIStorage.sol";

/**
 * @title Black Eyed Creatures Core Contract
 * @dev This is the base contract for Black Eyed Creatures (BEC).
 * @author https://42i.co
 */
contract BECCore is BECURIStorage {
  /// @dev Role constants for managing access to specific methods.
  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

  /// @dev Mapping between creature ID and the seed that generates its DNA.
  /// 0: Type (8, 3bit), Strategy (32, 5bit), Generation (4, 2bit), Seed (212bit)
  mapping(uint256 creatureId => bytes32 seed) internal creatureSeed;

  /// @dev Constructor initializes the ERC404 token with name "Black Eyed Creatures" and symbol "BEC".
  constructor() ERC404B("Black Eyed Creatures", "BEC", 333) {
    _setRoleAdmin(MINTER_ROLE, GENETICIST);
  }

  /**
   * @notice Mints new creatures with given DNA seeds for a specified address.
   * @dev Only addresses with MINTER_ROLE can call this function.
   * @param _creatureOwner Address of the new owner of the minted creatures.
   * @param _seed A seed value used to generate creature DNAs.
   * @param _quantity Quantity of minted creatures.
   * @return creatureIds Array of IDs for the newly minted creatures.
   */
  function mintWithSeed(
    address _creatureOwner,
    bytes32 _seed,
    uint256 _quantity
  ) public payable onlyRole(MINTER_ROLE) returns (uint256[] memory) {
    if (_quantity == 0) revert BECZeroQuantityMinting();
    uint256 baseCreatureId = nextTokenId();
    if (baseCreatureId + _quantity > 133333) revert BECCapLimitReached();
    uint256[] memory mintedCreatureIds = new uint256[](_quantity);

    _mint(_creatureOwner, _quantity);
    creatureSeed[baseCreatureId] = _seed;

    for (uint256 i = 0; i < _quantity; i++) {
      mintedCreatureIds[i] = baseCreatureId + i;
    }
    return mintedCreatureIds;
  }

  /**
   * @notice Retrieves the next available token ID for minting new creatures.
   * @dev This function returns the token ID that will be assigned to the next minted creature.
   * @return uint256 Next available token ID.
   */
  function nextTokenId() public view virtual returns (uint256) {
    return _nextTokenId;
  }

  /**
   * @notice Retrieves the seed of a creature by ID.
   * @param _creatureId ID of the creature.
   * @return bytes32 Seed of the creatures.
   */
  function getSeed(uint256 _creatureId) public view returns (bytes32) {
    bytes32 seed = 0;
    for (uint256 i = 0; seed == 0; i++) {
      seed = creatureSeed[_creatureId - i];
    }
    return seed;
  }

  /**
   * @notice Retrieves the seeds of a set creatures by ID.
   * @param _creatureIds ID of the creatures.
   * @return bytes32[] Seeds of the creatures.
   */
  function getSeeds(uint256[] calldata _creatureIds) public view returns (bytes32[] memory) {
    bytes32[] memory seeds = new bytes32[](_creatureIds.length);
    for (uint256 j = 0; j < _creatureIds.length; j++) {
      seeds[j] = getSeed(_creatureIds[j]);
    }
    return seeds;
  }

  /**
   * @notice Retrieves the total number of tokens currently in circulation.
   * @dev This function returns the total count of tokens that have been minted and are currently in existence.
   * @return uint256 Total number of tokens in circulation.
   */
  function totalTokens() public view returns (uint256) {
    return _totalTokens();
  }

  /**
   * @notice Burns a creature by ID.
   * @dev Only owner or approved senders can call this function.
   * @param _creatureId ID of the creature to be burned.
   */
  function burn(uint256 _creatureId) public {
    if (!_isApprovedOrOwner(msg.sender, _creatureId)) revert ERC721InsufficientApproval(msg.sender, _creatureId);
    _burn(_creatureId);
  }

  /**
   * @notice Retrieves the number of creatures that have been burned.
   * @return uint256 Number of burned creatures.
   */
  function burned() public view returns (uint256) {
    return _burned();
  }

  /**
   * @dev Forces the exemption status for ERC-721 transfers for the caller.
   *      This function can only be called by the owner, and is created for setting as exempt DEXs & LPs.
   *      An address that is not a contract, cannot be forced.
   * 
   * Emits:
   * - {Transfer} event for each target_ ERC721 token from/to the stash.
   * 
   * @param _target The address to be setted as exempt.
   * @param _state The new exemption state to set (true for exempt, false for non-exempt).
   */
  function forceERC721TransferExempt(address _target, bool _state) external onlyOwner {
    if (_target.code.length == 0) revert BECInvalidAddress(_target);
    _setERC721TransferExempt(_target, _state);
  }
}

File 2 of 35 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

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

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

File 3 of 35 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

File 4 of 35 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../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.
 *
 * The initial owner is set to the address provided by the deployer. 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;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @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 {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @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 {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _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 5 of 35 : draft-IERC6093.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

File 6 of 35 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

File 7 of 35 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 8 of 35 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

File 9 of 35 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

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

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

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

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

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

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

File 11 of 35 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

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

File 12 of 35 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 13 of 35 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

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

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

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

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

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

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

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

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

        return (signer, RecoverError.NoError, bytes32(0));
    }

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

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

File 14 of 35 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

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

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

pragma solidity ^0.8.20;

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 17 of 35 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 18 of 35 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

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

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

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

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

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

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

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

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 19 of 35 : BECAccessControl.sol
// contracts/BECAccessControl.sol
// SPDX-License-Identifier: MIT

/**

    /3333333     /33333333     /333333    
   | 33__  33   | 33_____/    /33__  33   
   | 33  \ 33   | 33         | 33  \__/   
   | 3333333    | 33333      | 33         
   | 33__  33   | 33__/      | 33         
   | 33  \ 33   | 33         | 33    33   
   | 3333333/   | 33333333   |  333333/   
   |_______/    |________/    \______/    

 # https://blackeyedcreatures.com

 */

pragma solidity ^0.8.25;

import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

/**
 * @title Black Eyed Creatures Access Control
 * @author https://42i.co
 */
abstract contract BECAccessControl is AccessControl {

  /// @dev Role identifier for the GENETICIST role.
  bytes32 public constant GENETICIST = keccak256("GENETICIST");

  /// @dev Constructor grants the GENETICIST role to the address deploying the contract.
  constructor() {
    _setRoleAdmin(GENETICIST, GENETICIST);
    _grantRole(GENETICIST, msg.sender);
  }
}

File 20 of 35 : BECEnabler.sol
// contracts/BECEnabler.sol
// SPDX-License-Identifier: MIT

/**

   /3333333    /33333333    /333333 
  | 33__  33  | 33_____/   /33__  33
  | 33  \ 33  | 33        | 33  \__/
  | 3333333   | 33333     | 33      
  | 33__  33  | 33__/     | 33      
  | 33  \ 33  | 33        | 33    33
  | 3333333/  | 33333333  |  333333/
  |_______/   |________/   \______/ 

 # https://blackeyedcreatures.com

 */

pragma solidity ^0.8.25;

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

/**
 * @title Black Eyed Creatures Enabler
 * @author https://42i.co
 */
contract BECEnabler is BECWithdrawable {

  event BECEnabled(address contractAddress);

  /// @dev Internal state variable to track whether the BEC ecosystem is enabled.
  bool internal enabled = false;

  /**
   * @notice Checks whether the contract enabled.
   * @return bool True if the contract is enabled, false otherwise.
   */
  function isEnabled() external view returns (bool) {
    return enabled;
  }

  /**
   * @notice Enables the BEC ecosystem functionalities.
   * @dev Sets the internal `enabled` state variable to true, activating the functionalities of the BEC ecosystem that depend on this state. 
   *      This action can typically control the operational state of various contract features, such as minting or token transfers.
   */
  function enable() external onlyOwner {
    enabled = true;
    emit BECEnabled(address(this));
  }
}

File 21 of 35 : BECGenesisEngine.sol
// contracts/BECGenesisEngine.sol
// SPDX-License-Identifier: MIT

/**

   /3333333    /33333333    /333333 
  | 33__  33  | 33_____/   /33__  33
  | 33  \ 33  | 33        | 33  \__/
  | 3333333   | 33333     | 33      
  | 33__  33  | 33__/     | 33      
  | 33  \ 33  | 33        | 33    33
  | 3333333/  | 33333333  |  333333/
  |_______/   |________/   \______/ 

 # http://blackeyedcreatures.com

 */

pragma solidity ^0.8.25;

import {BECCore} from "./BECCore.sol";
import {BECLab} from "./BECLab.sol";
import {BECWithdrawable} from "./BECWithdrawable.sol";
import {BECAccessControl} from "./BECAccessControl.sol";
import {IBECDnaStrategy} from "./strategies/IBECDnaStrategy.sol";
import {IBECMetaExtension} from "./meta-extensions/IBECMetaExtension.sol";
import {IERC721Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";

/**
 * @title Black Eyed Creatures Genesis Engine Contract
 * @notice Orchestrates the unveiling of creature DNA within the Black Eyed Creatures universe.
 * @dev Manages the revelation of creature DNA based on different logics.
 * @author https://42i.co
 */
contract BECGenesisEngine is BECWithdrawable, BECAccessControl, IERC721Errors {

  /// @dev A constant identifier for the role that is permitted to update the genesis storage.
  bytes32 public constant GENESIS_STORAGE_UPDATER = keccak256("GENESIS_STORAGE_UPDATER");

  /// @dev Core contract address.
  address private immutable coreContractAddress;

  /// @dev Nested mapping structure to store the probability information of each variation for each phenotype and family, represented bitwise.
  /// The outermost mapping categorizes by [distribution].
  /// The intermediate mapping categorizes by [phenotype].
  /// The innermost mapping categorizes by [family].
  /// The uint256 value associated with each [distribution][phenotype][family] contains:
  ///   - familyTickets (32b): Represents the quantity of tickets allocated to this specific family, influencing the probability of selection.
  ///   - variationsCount (8b): Indicates the total number of variations available within the family.
  ///   - variationExponent (3b): Specifies the bit length used to represent the rarity of each family and variation. This value acts as an exponent for calculating their respective ticket quantities.
  mapping(uint256 distribution => mapping(uint256 phenotype => mapping(uint256 family => uint256 tickets))) public tickets;

  /// @dev A mapping storing uint256s, each containing 32-bit segments representing the total tickets for each phenotype in a specific distribution.
  mapping(uint256 distribution => uint256 tickets) public phenotypeTickets;

  /// @dev A mapping to store specific data associated with each creature, indexed by the creature's ID.
  mapping(uint256 creatureId => uint256 value) public genesisStorage;

  /// @dev A mapping that associates each dna strategy with a strategy contract address, indexed by the strategy ID.
  mapping(uint256 strategyId => address contractAddress) public dnaStrategies;

  /// @dev An array of metadata extensions.
  IBECMetaExtension[] public extensions;

  /**
   * @notice Initializes the Genesis Engine.
   * @dev Sets the coreContractAddress to the provided BEC core contract address.
   * @param _becCore The address of the BEC core contract.
   */
  constructor(address _becCore) {
    coreContractAddress = _becCore;
    _setRoleAdmin(GENESIS_STORAGE_UPDATER, GENETICIST);
  }

  /**
   * @notice Sets the genesis storage value for a specific creature.
   * @dev The function requires that the storage value for the creature has not been set previously. It can only be called by an address with the GENESIS_STORAGE_UPDATER role.
   * @param _creatureId The ID of the creature for which the storage value is to be set.
   * @param _value The value to be stored.
   */
  function setGenesisStorage(uint256 _creatureId, uint256 _value) public onlyRole(GENESIS_STORAGE_UPDATER) {
    if (genesisStorage[_creatureId] != 0) revert BECGenesisStoreAlreadySet(_creatureId);
    genesisStorage[_creatureId] = _value;
  }

  /**
   * @notice Retrieves the DNA of a specific creature.
   * @dev The function fetches the seed associated with the creature from the core contract, 
   *      determines the strategy using the seed, and then retrieves the DNA using the strategy. 
   *      It requires that the creature with the given ID exists.
   * @param _creatureId The ID of the creature whose DNA is to be retrieved.
   * @return uint256 The DNA of the specified creature.
   */
  function getDna(uint256 _creatureId) public view returns (uint256) {
    BECCore coreContract = BECCore(payable(coreContractAddress));
    if (!coreContract.exists(_creatureId)) revert ERC721NonexistentToken(_creatureId);
    bytes32 seed = coreContract.getSeed(_creatureId);
    IBECDnaStrategy strategy = IBECDnaStrategy(dnaStrategies[uint256(uint8(seed[0]) >> 3) & 0x1F]);
    return strategy.getDna(_creatureId);
  }

  /**
   * @notice Retrieves the token URI of a specific creature.
   * @dev The function fetches the seed associated with the creature from the core contract, determines the strategy using the seed, and then retrieves the token URI using the strategy.
   * @param _creatureId The ID of the creature whose token URI is to be retrieved.
   * @return string The token URI of the specified creature.
   */
  function tokenURI(uint256 _creatureId) public view returns (string memory) {
    BECCore coreContract = BECCore(payable(coreContractAddress));
    bytes32 seed = coreContract.getSeed(_creatureId);
    IBECDnaStrategy strategy = IBECDnaStrategy(dnaStrategies[uint256(uint8(seed[0]) >> 3) & 0x1F]);
    return strategy.tokenURI(_creatureId);
  }

  /**
   * @notice Retrieves the genesis storage value for a specific creature.
   * @dev Returns the stored value associated with the given creature ID from the genesisStorage mapping.
   * @param _creatureId The ID of the creature whose genesis storage value is to be retrieved.
   * @return uint256 The genesis storage value of the specified creature.
   */
  function getGenesisStorage(uint256 _creatureId) public view returns (uint256) {
    return genesisStorage[_creatureId];
  }

  /**
   * @notice Retrieves the amount of tickets of a family for a specific phenotype within a given distribution ID.
   * @dev Accesses the tickets mapping with the provided _distribution, _phenotype, and _family to return the associated family ticket information.
   * @param _distribution The distribution identifier.
   * @param _phenotype The phenotype identifier.
   * @param _family The family identifier within the phenotype.
   * @return uint256 The family ticket information of the specified phenotype and family.
   */
  function getFamilyTickets(uint256 _distribution, uint256 _phenotype, uint256 _family) public view returns (uint256) {
    return tickets[_distribution][_phenotype][_family] & 0xFFFFFFFF;
  }

  /**
   * @notice Retrieves the amount of tickets of a variation for a specific phenotype and family within a given distribution ID.
   * @dev Calculates the variation ticket information based on the stored ticket information and the provided _variation.
   * @param _distribution The distribution identifier.
   * @param _phenotype The phenotype identifier.
   * @param _family The family identifier within the phenotype.
   * @param _variation The variation identifier within the family.
   * @return uint256 The variation ticket information of the specified phenotype, family, and variation.
   */
  function getVariationTickets(uint256 _distribution, uint256 _phenotype, uint256 _family, uint256 _variation) public view returns (uint256) {
    return 1 << ((tickets[_distribution][_phenotype][_family] >> (40 + (_variation * 3))) & 0x7);
  }

  /**
   * @notice Retrieves the count of variations for a specific phenotype and family within a given distribution ID.
   * @dev Calculates the variations count based on the stored ticket information.
   * @param _distribution The distribution identifier.
   * @param _phenotype The phenotype identifier.
   * @param _family The family identifier within the phenotype.
   * @return uint256 The count of variations for the specified phenotype and family.
   */
  function getVariationsCount(uint256 _distribution, uint256 _phenotype, uint256 _family) public view returns (uint256) {
    return (tickets[_distribution][_phenotype][_family] >> 32) & 0xFF;
  }

  /**
   * @notice Retrieves the total number of tickets for each phenotype within a given distribution.
   * @dev Accesses the ticketsPerPhenotype mapping with the provided _distribution ID to return the associated ticket count.
   * @param _distribution The ID of the distribution.
   * @return uint256 The total number of tickets for each phenotype in the specified distribution.
   */
  function getPhenotypeTickets(uint256 _distribution) public view returns (uint256) {
    return phenotypeTickets[_distribution];
  }

  /**
   * @notice Adds ticket information for a specific distribution.
   * @dev Requires that the distribution has not been previously set. Iterates through the _tickets parameter to populate the tickets mapping for the given distribution. Can only be called by an address with the GENETICIST role.
   * @param _distribution The ID of the distribution to which the tickets are to be added.
   * @param _phenotypeTickets The total number of tickets for each phenotype within the distribution.
   * @param _tickets A two-dimensional array representing the ticket information to be added for each phenotype and family.
   */
  function addTickets(uint256 _distribution, uint256 _phenotypeTickets, uint256[][] memory _tickets) public onlyRole(GENETICIST) {
    if (phenotypeTickets[_distribution] != 0) revert BECDistributionAlreadySet(_distribution);
    phenotypeTickets[_distribution] = _phenotypeTickets;
    for (uint256 i = 0; i < _tickets.length; i++) {
      for (uint256 j = 0; j < _tickets[i].length; j++) {
        tickets[_distribution][i][j] = _tickets[i][j];
      }
    }
  }

  /**
   * @notice Registers a DNA strategy for a distribution.
   * @dev Can only be called by an address with the GENETICIST role.
   * @param _dnaStrategy The address of the DNA strategy contract to be registered.
   */
  function registerStrategy(address _dnaStrategy) public onlyRole(GENETICIST) {
    IBECDnaStrategy dnaStrategy = IBECDnaStrategy(_dnaStrategy);
    uint256 distributionId = dnaStrategy.getDistributionId();
    if (tickets[distributionId][0][0] == 0) revert BECWrongDistributionId(distributionId);

    uint256 strategyId = dnaStrategy.getStrategyId();
    dnaStrategies[strategyId] = _dnaStrategy;
  }

  /**
   * @notice Adds a new metadata extension to the Genesis Engine.
   * @dev Appends a new extension contract address to the `extensions` array. 
   *      This function can only be invoked by an address with the GENETICIST role. 
   *      Each extension should implement the IBECMetaExtension interface.
   * @param _extensionAddress The address of the metadata extension contract to add.
   */
  function addExtension(address _extensionAddress) public onlyRole(GENETICIST) {
    IBECMetaExtension extension = IBECMetaExtension(_extensionAddress);
    extensions.push(extension);
  }

  /**
   * @notice Clears all registered metadata extensions from the Genesis Engine.
   * @dev Deletes the `extensions` array, effectively removing all metadata extension contracts. 
   *      This action cannot be undone. This function can only be invoked by an address with the GENETICIST role.
   */
  function clearExtensions() public onlyRole(GENETICIST) {
    delete extensions;
  }

  /**
   * @notice Retrieves the addresses of all registered metadata extensions.
   * @dev Returns an array of addresses for each metadata extension contract currently registered in the `extensions` array.
   * @return An array of IBECMetaExtension contract addresses currently registered.
   */
  function getExtensions() external view returns (IBECMetaExtension[] memory) {
    return extensions;
  }
}

File 22 of 35 : BECLab.sol
// contracts/BECLab.sol
// SPDX-License-Identifier: MIT

/**

   /3333333    /33333333    /333333 
  | 33__  33  | 33_____/   /33__  33
  | 33  \ 33  | 33        | 33  \__/
  | 3333333   | 33333     | 33      
  | 33__  33  | 33__/     | 33      
  | 33  \ 33  | 33        | 33    33
  | 3333333/  | 33333333  |  333333/
  |_______/   |________/   \______/ 

 # https://blackeyedcreatures.com

 */

pragma solidity ^0.8.25;

import {BECCore} from "./BECCore.sol";
import {BECEnabler} from "./BECEnabler.sol";
import {BECAccessControl} from "./BECAccessControl.sol";
import {BECGenesisEngine} from "./BECGenesisEngine.sol";
import {EIP712} from "./eip712/EIP712.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {IERC721Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";

/**
 * @title Black Eyed Creatures Lab Contract
 * @author https://42i.co
 */
contract BECLab is BECEnabler, BECAccessControl, EIP712, IERC721Errors {
  using ECDSA for bytes32;

  event FamilyNameSet(uint256 indexed phenotype, uint256 indexed family, uint256[] ambassadors, string name);
  event VariationNameSet(uint256 indexed phenotype, uint256 indexed family, uint256 indexed variation, uint256[] ambassadors, string name);
  event CreatureNameSet(uint256 indexed creatureId, string name);

  /// @dev Core contract address.
  address private immutable coreContractAddress;

  /// @dev Genesis engine contract address.
  address private immutable genesisEngineAddress;

  /// @dev Blcksphr contract address.
  address private immutable blcksphrsAddress;

  /// @dev Mapping of the lab registry
  /// Structure:
  /// [bucket] each bucket stores 8 creature registries, of 4 bytes each
  /// - isVariationAmbassador (1bit)
  /// - isFamilyAmbassador (1bit)
  /// - ambassadorPhenotype (3bit)
  /// - ambassadorFamily (5bit)
  /// - ambassadorVariation (6bit)
  /// - omegas (16bit)
  mapping(uint256 bucket => uint256 registries) internal labRegistry;

  /// @dev Mapping that keeps track of each family name.
  mapping(uint256 phenotype => mapping(uint256 family => string name)) public familyNames;

  /// @dev Mapping that keeps track of each variation name.
  mapping(uint256 phenotype => mapping(uint256 family => mapping(uint256 variation => string name))) public variationNames;

  /// @dev Names of the creatures.
  mapping(uint256 creatureId => string name) public names;

  /// @dev Hash of names, and if they are used or not (default not).
  mapping(bytes32 nameHash => bool isUsed) internal nameUsed;

  /// @dev Mapping storing the amount of families and variations set by the address.
  mapping(address caller => uint256 count) public familyVariationAndNamesSet;

  /// @dev A constant identifier for the role that is permitted to update the lab register.
  bytes32 public constant LAB_REGISTER_UPDATER = keccak256("LAB_REGISTER_UPDATER");

  /// @dev The typehash for the data type specified in the family structured data.
  /// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#rationale-for-typehash
  bytes32 public constant VERIFY_FAMILY_TYPEHASH = keccak256("VerifyFamilyName(uint256 phenotype,uint256 family,uint256[] ambassadors,string name,address wallet)");

  /// @dev The typehash for the data type specified in the variation structured data.
  /// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#rationale-for-typehash
  bytes32 public constant VERIFY_VARIATION_TYPEHASH = keccak256("VerifyVariationName(uint256 phenotype,uint256 family,uint256 variation,uint256[] ambassadors,string name,address wallet)");

  /**
   * @dev Constructor for the Lab contract.
   * @param _becCore The core contract address.
   * @param _becGenesisEngine The genesis engine contract address.
   * @param _blcksphrsAddress The blcksphr contract address.
   */
  constructor(address _becCore, address _becGenesisEngine, address _blcksphrsAddress) {
    coreContractAddress = _becCore;
    genesisEngineAddress = _becGenesisEngine;
    blcksphrsAddress = _blcksphrsAddress;
    _setRoleAdmin(LAB_REGISTER_UPDATER, GENETICIST);
  }

  /**
   * @notice Checks if a variation name proposal meets the necessary criteria for a given phenotype, family, and variation.
   * @dev This function examines the validity of a name proposal for a variation by assessing the criteria regarding
   *      the phenotype, family, variation, and the three proposed ambassador creatures.
   *      A successful execution of this function indicates the name proposal meets all established conditions.
   * @param _phenotype The phenotype within which the family resides.
   * @param _family The family within which the variation exists.
   * @param _variation The specific variation for which the name validity is being verified.
   * @param _ambassadorIds The 3 ambassador ids.
   * @param _name The proposed name for the variation.
   *
   * Requirements:
   * - Phenotype number must be less than 6.
   * - Name length should be up to 8 characters.
   * - Name must be alphanumeric.
   * - The proposed ambassador creatures must be distinct.
   * - The creatures cannot already be variation ambassadors.
   * - Each proposed ambassador must be associated with the specified phenotype, family, and variation.
   */
  function verifyVariationName(
    uint256 _phenotype,
    uint256 _family,
    uint256 _variation,
    uint256[] memory _ambassadorIds,
    string memory _name
  ) public view {
    // Verify basics
    if (_phenotype > 5) revert BECInvalidPhenotype(_phenotype);
    if (bytes(_name).length > 8 || bytes(_name).length == 0) revert BECInvalidName(_name);
    if (!isAZ(bytes(_name))) revert BECInvalidName(_name);
    if (nameUsed[keccak256(abi.encodePacked(_name))]) revert BECDuplicatedName(_name);
    if (_ambassadorIds.length != 3) revert BECInvalidAmbassadorsLength(_ambassadorIds.length);
    if (bytes(variationNames[_phenotype][_family][_variation]).length != 0)
      revert BECVariationNameAlreadySet(_phenotype, _family, _variation);

    // Verify creatures
    if (
      _ambassadorIds[0] == _ambassadorIds[1] ||
      _ambassadorIds[0] == _ambassadorIds[2] ||
      _ambassadorIds[1] == _ambassadorIds[2]
    ) revert BECInvalidAmbassadors(_ambassadorIds[0], _ambassadorIds[1], _ambassadorIds[2]);

    // Verifying ambassadors and family & variation for each creature
    for (uint256 i = 0; i < 3; i++) {
      uint256 registry = (labRegistry[_ambassadorIds[i] / 8] >> ((_ambassadorIds[i] % 8) * 32)) & 0xFFFFFFFF;
      if (registry & 0x1 != 0) revert BECAlreadyVariationAmbassador(_ambassadorIds[i]);
      verifyFamilyAndVariation(_phenotype, _family, _variation, _ambassadorIds[i]);
    }
  }

  /**
   * @notice Verifies if a creature's family and variation match the provided family and variation within a specified phenotype.
   * @dev Extracts the creature's DNA related to the provided phenotype. It then verifies if the extracted DNA matches the provided family and variation.
   *      A successful execution indicates that the creature has the expected family and variation within the specified phenotype.
   * @param _phenotype The phenotype within which the family and variation reside.
   * @param _family The expected family of the creature within the given phenotype.
   * @param _variation The expected variation of the creature within the provided family.
   * @param _creatureId The ID of the creature (creature) to be verified.
   *
   * Requirements:
   * - The creature's family and variation, as determined from its DNA for the given phenotype, must match `_family` and `_variation` respectively.
   */
  function verifyFamilyAndVariation(
    uint256 _phenotype,
    uint256 _family,
    uint256 _variation,
    uint256 _creatureId
  ) internal view {
    unchecked {
      uint256 phenotypeTraits = BECGenesisEngine(payable(genesisEngineAddress)).getDna(_creatureId) >> ((_phenotype * 16));
      uint256 dnaVariation = (phenotypeTraits >> 8) & 0xFF;
      uint256 dnaFamily = (phenotypeTraits) & 0xFF;

      if (dnaFamily != _family) revert BECInvalidFamily(_creatureId);
      if (dnaVariation != _variation) revert BECInvalidVariation(_creatureId);
    }
  }

  /**
   * @notice Allows a user to propose a name for a variation by specifying the phenotype, family, variation, ambassador creatures,
   *         and the name itself.
   * @dev This function allows users to suggest a name for a specific variation by proposing three creatures as ambassadors.
   *      Before calling this function, users should call verifyVariationNameProposal() with the same parameters
   *      to ensure that the proposal is valid.
   *      The proposal will be checked by a validator backend after submission.
   *      The proposal is validated through a signature. If the proposal meets all criteria, the name is set for the variation.
   * @param _phenotype The phenotype within which the family resides.
   * @param _family The family within which the variation exists.
   * @param _variation The specific variation for which a name is being proposed.
   * @param _ambassadorIds An array of ambassador creature IDs proposed for the naming.
   * @param _name The proposed name for the variation.
   *
   * Requirements:
   * - Phenotype number must be less than 6.
   * - Name length should be up to 8 characters.
   * - Name must be alphanumeric.
   * - The proposal price must be met.
   * - The caller must own all three proposed ambassador creatures.
   * - The proposed ambassador creatures must be distinct.
   * - The creatures cannot already be variation ambassadors.
   * - There shouldn't be a pre-existing name or proposal for the specified variation.
   */
  function setVariationName(
    uint256 _phenotype,
    uint256 _family,
    uint256 _variation,
    uint256[] memory _ambassadorIds,
    string memory _name,
    bytes calldata _signature
  ) public {
    if (!enabled) revert BECNotEnabled();
    // Verify basics
    if (!validateVariationSignature(_phenotype, _family, _variation, _ambassadorIds, _name, _signature)) revert EIP712InvalidSignature(_signature);
    if (bytes(_name).length > 8 || bytes(_name).length == 0) revert BECInvalidName(_name);
    if (nameUsed[keccak256(abi.encodePacked(_name))]) revert BECDuplicatedName(_name);
    if (bytes(variationNames[_phenotype][_family][_variation]).length != 0)
      revert BECVariationNameAlreadySet(_phenotype, _family, _variation);

    BECCore coreContract = BECCore(payable(coreContractAddress));
    for (uint256 i = 0; i < 3; i++) {
      // Verify ownership
      if (msg.sender != coreContract.ownerOf(_ambassadorIds[i])) revert BECInvalidOwner(msg.sender, _ambassadorIds[i]);

      // Verifying creature was not used for being ambassador
      uint256 registry = (labRegistry[_ambassadorIds[i] / 8] >> ((_ambassadorIds[i] % 8) * 32)) & 0xFFFFFFFF;
      if (registry & 0x1 != 0) revert BECAlreadyVariationAmbassador(_ambassadorIds[i]);

      // Setting creature as ambassador in the registry
      setVariationAmbassadorToRegistry(_ambassadorIds[i], _phenotype, _family, _variation);
    }

    // Set variation name
    variationNames[_phenotype][_family][_variation] = _name;
    nameUsed[keccak256(abi.encodePacked(_name))] = true;
    familyVariationAndNamesSet[msg.sender] += 1;

    emit VariationNameSet(_phenotype, _family, _variation, _ambassadorIds, _name);
  }

  /**
   * @notice Validates the signature for a variation name proposal.
   * @dev Checks if the provided signature is valid for the given variation name proposal details.
   * @param _phenotype The phenotype ID involved in the proposal.
   * @param _family The family ID involved in the proposal.
   * @param _variation The variation ID for which the name is proposed.
   * @param _ambassadorIds The IDs of the ambassador creatures proposed.
   * @param _name The proposed name for the variation.
   * @param _signature The signature to validate.
   * @return bool Returns true if the signature is valid, false otherwise.
   */
  function validateVariationSignature(
    uint256 _phenotype,
    uint256 _family,
    uint256 _variation,
    uint256[] memory _ambassadorIds,
    string memory _name,
    bytes calldata _signature
  ) internal view returns (bool) {
    if (allowlistSigningKey == address(0)) revert EIP712BECSigningKeyNotSet();

    bytes32 digest = keccak256(
      abi.encodePacked(
        "\x19\x01",
        EIP712_DOMAIN_SEPARATOR,
        keccak256(
          abi.encode(
            VERIFY_VARIATION_TYPEHASH,
            _phenotype,
            _family,
            _variation,
            keccak256(abi.encodePacked(_ambassadorIds)),
            keccak256(bytes(_name)),
            msg.sender
          )
        )
      )
    );

    address recoveredAddress = digest.recover(_signature);
    return recoveredAddress == allowlistSigningKey;
  }

  /**
   * @notice Updates the registry entry to set the creature as a variation ambassador.
   * @dev This function performs bitwise operations on the provided registry entry to indicate
   *      that the creature is now an ambassador for a specified variation. It updates and returns
   *      the modified registry entry by setting the appropriate bits based on the phenotype, family,
   *      and variation passed as arguments.
   * @param _creatureId The creature Id.
   * @param _variationAmbassadorPhenotype The phenotype associated with the variation ambassador.
   * @param _variationAmbassadorFamily The family associated with the variation ambassador.
   * @param _variationAmbassadorVariation The specific variation the creature is an ambassador for.
   */
  function setVariationAmbassadorToRegistry(
    uint256 _creatureId,
    uint256 _variationAmbassadorPhenotype,
    uint256 _variationAmbassadorFamily,
    uint256 _variationAmbassadorVariation
  ) private {
    unchecked {
      labRegistry[_creatureId / 8] |=
        (1 |
          (_variationAmbassadorPhenotype << 2) |
          (_variationAmbassadorFamily << 5) |
          (_variationAmbassadorVariation << 10)) <<
        ((_creatureId % 8) * 32);
    }
  }

  /**
   * @notice Retrieves the name of a specified variation. If the variation hasn't been approved by ambassadors,
   *         it returns a default name based on the family and variation numbers.
   * @dev Checks the approval status of the variation using the variationAmbassadorsProposal mapping. If approved,
   *      it fetches the name from the variationNames mapping. Otherwise, it constructs a default name.
   * @param _phenotype The phenotype of the variation.
   * @param _family The family of the variation.
   * @param _variation The specific variation number.
   * @return variationName The name of the variation or a default name if not approved.
   */
  function getVariationName(
    uint256 _phenotype,
    uint256 _family,
    uint256 _variation
  ) public view returns (string memory) {
    if (bytes(variationNames[_phenotype][_family][_variation]).length != 0) {
      return capitalized(variationNames[_phenotype][_family][_variation]);
    }
    return string(abi.encodePacked("#(", Strings.toString(_family), ", ", Strings.toString(_variation), ")"));
  }

  /**
   * @notice Checks if a family name proposal meets the necessary criteria for a given phenotype and family.
   * @dev This function examines the validity of a name proposal for a family by assessing the criteria regarding
   *      the phenotype, family, and the three proposed ambassador creatures.
   *      A successful execution of this function indicates the name proposal meets all established conditions.
   *      The checks include validations for phenotype number, name length, name format,
   *      ambassador uniqueness, and ambassador association with the specific phenotype and family.
   *      It also ensures all variations within the family have been named before a family name is proposed.
   * @param _phenotype The phenotype within which the family resides.
   * @param _family The family for which the name validity is being verified.
   * @param _ambassadorIds An array of ambassador creature IDs proposed for the naming.
   * @param _name The proposed name for the family.
   *
   * Requirements:
   * - Phenotype number must be less than 6.
   * - Name length should be up to 8 characters.
   * - Name must be alphanumeric.
   * - The proposed ambassador creatures must be distinct.
   * - Each proposed ambassador must be associated with the specified phenotype and family.
   * - All variations within the family must have been named.
   *
   * @return Returns `true` if the name proposal is valid; reverts otherwise.
   */
  function verifyFamilyName(
    uint256 _phenotype,
    uint256 _family,
    uint256[] memory _ambassadorIds,
    string memory _name
  ) public view returns (bool) {
    unchecked {
      // Verify basics
      if (_phenotype > 5) revert BECInvalidPhenotype(_phenotype);
      if (bytes(_name).length > 8 || bytes(_name).length == 0) revert BECInvalidName(_name);
      if (!isAZ(bytes(_name))) revert BECInvalidName(_name);
      if (nameUsed[keccak256(abi.encodePacked(_name))]) revert BECDuplicatedName(_name);
      if (_ambassadorIds.length != 3) revert BECInvalidAmbassadorsLength(_ambassadorIds.length);
      if (bytes(familyNames[_phenotype][_family]).length != 0) revert BECFamilyNameAlreadySet(_phenotype, _family);

      BECGenesisEngine genesisEngineContract = BECGenesisEngine(payable(genesisEngineAddress));

      // Verify all variations of the family set up
      uint256 variationsCount = genesisEngineContract.getVariationsCount(0, _phenotype, _family);
      for (uint256 v = 0; v < variationsCount; v++) {
        if (bytes(variationNames[_phenotype][_family][v]).length == 0) revert BECNotAllVariationsSet(_phenotype, _family, v);
      }

      // Verify creatures are different
      if (
        _ambassadorIds[0] == _ambassadorIds[1] ||
        _ambassadorIds[0] == _ambassadorIds[2] ||
        _ambassadorIds[1] == _ambassadorIds[2]
      ) revert BECInvalidAmbassadors(_ambassadorIds[0], _ambassadorIds[1], _ambassadorIds[2]);

      // Verifying ambassadors and family & variation for each creature
      for (uint256 i = 0; i < 3; i++) {
        uint256 registry = (labRegistry[_ambassadorIds[i] / 8] >> ((_ambassadorIds[i] % 8) * 32)) & 0xFFFFFFFF;
        verifyFamilyAmbassador(_ambassadorIds[i], registry);
        verifyFamily(_phenotype, _family, _ambassadorIds[i]);
      }

      return true;
    }
  }

  /**
   * @notice Validates if a creature can be designated as a family ambassador.
   * @dev Evaluates the provided creature's registry to determine if the creature can be designated as a family ambassador.
   *      A successful execution indicates that the creature meets the criteria to become a family ambassador,
   *      i.e., it is not already a family ambassador and it is a variation ambassador.
   * @param _creatureId The creatureId to be verified.
   * @param _registry The registry entry of the creature (creature) to be verified.
   *
   * Requirements:
   * - The creature should not already be a family ambassador.
   * - The creature must be a variation ambassador.
   */
  function verifyFamilyAmbassador(uint256 _creatureId, uint256 _registry) internal pure {
    unchecked {
      if ((_registry & 2) != 0) revert BECAlreadyFamilyAmbassador(_creatureId); // Checks that the second bit (isFamilyAmbassador) is 0
      if ((_registry & 1) == 0) revert BECNotVariationAmbassador(_creatureId); // Checks that the first bit (isVariationAmbassador) is 1
    }
  }

  /**
   * @notice Verifies if the family of a creature corresponds to the specified family within a given phenotype.
   * @dev Extracts the creature's DNA related to the given phenotype and checks if its family matches the provided `_family`.
   *      A successful execution indicates the creature belongs to the expected family within the specified phenotype.
   * @param _phenotype The phenotype within which the family resides.
   * @param _family The expected family of the creature within the given phenotype.
   * @param _creatureId The ID of the creature (creature) to be verified.
   *
   * Requirements:
   * - The creature's family, as determined from its DNA for the given phenotype, must match `_family`.
   */
  function verifyFamily(uint256 _phenotype, uint256 _family, uint256 _creatureId) internal view {
    unchecked {
      uint256 phenotypeTraits = BECGenesisEngine(payable(genesisEngineAddress)).getDna(_creatureId) >>
        ((_phenotype * 16));
      uint256 dnaFamily = (phenotypeTraits) & 0xFF;
      if (dnaFamily != _family) revert BECInvalidFamily(_creatureId);
    }
  }

  /**
   * @notice Proposes a name for a family within a specific phenotype.
   * @dev This function allows users to suggest a name for a specific family by proposing three creatures as ambassadors.
   *      The proposal will be checked by a validator backend after submission. If valid, the name will be approved.
   *      The function includes basic validation for phenotype number, name length, name format, proposal price, 
   *      creature ownership, ambassador uniqueness, and previous proposals for the same family.
   * @param _phenotype The phenotype within which the family resides.
   * @param _family The family for which a name is being proposed.
   * @param _ambassadorIds An array of ambassador creature IDs proposed for the naming.
   * @param _name The proposed name for the family.
   *
   * Requirements:
   * - Phenotype number must be less than 6.
   * - Name length should be up to 8 characters.
   * - Name must be alphanumeric.
   * - The proposal price must be met.
   * - The caller must own all three proposed ambassador creatures.
   * - The proposed ambassador creatures must be distinct.
   * - All family variations should have their names set.
   * - The creatures cannot already be family ambassadors.
   * - There shouldn't be a pre-existing name or proposal for the specified family.
   */
  function setFamilyName(
    uint256 _phenotype,
    uint256 _family,
    uint256[] memory _ambassadorIds,
    string memory _name,
    bytes calldata _signature
  ) public {
    if (!enabled) revert BECNotEnabled();
    // Verify not already set
    if (!validateFamilySignature(_phenotype, _family, _ambassadorIds, _name, _signature)) revert EIP712InvalidSignature(_signature);
    if (bytes(_name).length > 8 || bytes(_name).length == 0) revert BECInvalidName(_name);
    if (nameUsed[keccak256(abi.encodePacked(_name))]) revert BECDuplicatedName(_name);
    if (bytes(familyNames[_phenotype][_family]).length != 0) revert BECFamilyNameAlreadySet(_phenotype, _family);

    BECCore coreContract = BECCore(payable(coreContractAddress));
    for (uint256 i = 0; i < 3; i++) {
      if (msg.sender != coreContract.ownerOf(_ambassadorIds[i])) revert BECInvalidOwner(msg.sender, _ambassadorIds[i]);
      uint256 registry = (labRegistry[_ambassadorIds[i] / 8] >> ((_ambassadorIds[i] % 8) * 32)) & 0xFFFFFFFF;
      verifyFamilyAmbassador(_ambassadorIds[i], registry);
      setFamilyAmbassadorToRegistry(_ambassadorIds[i], _phenotype, _family);
    }

    familyNames[_phenotype][_family] = _name;
    nameUsed[keccak256(abi.encodePacked(_name))] = true;
    familyVariationAndNamesSet[msg.sender] += 1 << 64;

    emit FamilyNameSet(_phenotype, _family, _ambassadorIds, _name);
  }

  /**
   * @notice Validates the signature for a family name proposal.
   * @dev Checks if the provided signature is valid for the given family name proposal details.
   * @param _phenotype The phenotype ID involved in the proposal.
   * @param _family The family ID for which the name is proposed.
   * @param _ambassadorIds The IDs of the ambassador creatures proposed.
   * @param _name The proposed name for the family.
   * @param _signature The signature to validate.
   * @return bool Returns true if the signature is valid, false otherwise.
   */
  function validateFamilySignature(
    uint256 _phenotype,
    uint256 _family,
    uint256[] memory _ambassadorIds,
    string memory _name,
    bytes calldata _signature
  ) internal view returns (bool) {
    if (allowlistSigningKey == address(0)) revert EIP712BECSigningKeyNotSet();

    bytes32 digest = keccak256(
      abi.encodePacked(
        "\x19\x01",
        EIP712_DOMAIN_SEPARATOR,
        keccak256(
          abi.encode(
            VERIFY_FAMILY_TYPEHASH,
            _phenotype,
            _family,
            keccak256(abi.encodePacked(_ambassadorIds)),
            keccak256(bytes(_name)),
            msg.sender
          )
        )
      )
    );

    address recoveredAddress = digest.recover(_signature);
    return recoveredAddress == allowlistSigningKey;
  }

  /**
   * @notice Retrieves the name of a family within a specific phenotype.
   * @dev This function returns the name of a family based on the given phenotype and family parameters.
   *      If the ambassador proposal for the family name has been approved, it returns the assigned family name.
   *      Otherwise, it returns a default placeholder name constructed with the family number.
   * @param _phenotype The phenotype within which the family resides.
   * @param _family The specific family for which the name is being queried.
   * @return A string representing either the assigned name of the family (if the name proposal was approved)
   *         or a default placeholder name indicating the family number.
   */
  function getFamilyName(uint256 _phenotype, uint256 _family) public view returns (string memory) {
    if (bytes(familyNames[_phenotype][_family]).length != 0) {
      return capitalized(familyNames[_phenotype][_family]);
    }
    return string(abi.encodePacked("#(", Strings.toString(_family), ")"));
  }

  /**
   * @notice Updates the registry entry to set the creature as a family ambassador.
   * @dev This function performs bitwise operations on the provided registry entry to indicate
   *      that the creature is now an ambassador for a specified family. It updates and returns
   *      the modified registry entry by setting the appropriate bits based on the laphenotypeyer and family
   *      passed as arguments.
   * @param _creatureId The creature Id.
   * @param _phenotype The phenotype associated with the family ambassador.
   * @param _family The specific family the creature is an ambassador for.
   */
  function setFamilyAmbassadorToRegistry(uint256 _creatureId, uint256 _phenotype, uint256 _family) private {
    unchecked {
      labRegistry[_creatureId / 8] |= (2 | (_phenotype << 2) | (_family << 5)) << ((_creatureId % 8) * 32);
    }
  }

  /**
   * @notice Determines whether the provided string consists only of A-Z characters.
   * @dev The function checks each character of the input string to ensure it's a letter (A-Z).
   *      Non-alphabetic characters will result in a return value of false.
   *      ^[A-Z]{0,6}$
   * @param _string The input string, given as a byte array, to be verified.
   * @return bool Returns true if all characters in the string are A-Z, otherwise returns false.
   */
  function isAZ(bytes memory _string) internal pure returns (bool) {
    unchecked {
      if (_string.length < 3 || _string.length > 7) {
        return false;
      }
      for (uint256 i = 0; i < _string.length; i++) {
        bytes1 char = _string[i];
        if (char < 0x41 || char > 0x5A) { // not A-Z
          return false;
        }
      }
      return true;
    }
  }

  /**
   * @notice Removes the capitalization of all characters besides the first one.
   * @dev This function iterates over all the elements of the string array and transforms it to lowercase.
   * @param _caps the string all in caps.
   * @return A string capitalized. (First leter in uppercase, the rest in lowercase).
   */
  function capitalized(string memory _caps) internal pure returns (string memory) {
    bytes memory b = bytes(_caps);
    for (uint i = 1; i < b.length; i++) {
      if (uint8(b[i]) >= 65 && uint8(b[i]) <= 90) {
        b[i] = bytes1(uint8(b[i]) + 32);
      }
    }
    return string(b);
  }

  /**
   * @notice Sets the allowlist signing key for validating signatures.
   * @dev Updates the allowlist signing key used for signature validation in name proposals.
   * @param newSigningKey The new signing key to be used for validation.
   */
  function setAllowlistSigningAddress(address newSigningKey) public onlyRole(GENETICIST) {
    _setAllowlistSigningAddress(newSigningKey);
  }

  /**
   * @notice Retrieves the lab registry record for a specific creature.
   * @dev Fetches the registry record for a creature based on its ID.
   *      The record contains information about the creature's ambassador status and other traits.
   * @param _creatureId The ID of the creature.
   * @return uint256 The registry record of the specified creature.
   */
  function getLabRegistryRecord(uint256 _creatureId) external view returns (uint256) {
    return (labRegistry[_creatureId / 8] >> ((_creatureId % 8) * 32)) & 0xFFFFFFFF;
  }

  /**
   * @notice Retrieves the number of Omegas for a specific creature.
   * @dev Fetches the Omega count for a creature from the lab registry based on its ID.
   * @param _creatureId The ID of the creature.
   * @return uint256 The number of Omegas associated with the creature.
   */
  function getOmegas(uint256 _creatureId) external view returns (uint256) {
    return (labRegistry[_creatureId / 8] >> ((((_creatureId % 8) * 32)) + 16)) & 0xFFFF;
  }

  /**
   * @notice Increments the Omega count for a specific creature.
   * @dev Adds a specified quantity to the Omega count of a creature in the lab registry.
   * @param _creatureId The ID of the creature.
   * @param _quantity The quantity to add to the creature's Omega count.
   */
  function incrementOmegas(uint256 _creatureId, uint256 _quantity) external onlyRole(LAB_REGISTER_UPDATER) {
    labRegistry[_creatureId / 8] += _quantity << (16 + ((_creatureId % 8) * 32));
  }

  /**
   * @notice Grants the LAB_REGISTER_UPDATER role to a specified address.
   * @dev Assigns the role responsible for updating lab registry entries to a given address.
   * @param _address The address to be granted the LAB_REGISTER_UPDATER role.
   */
  function grantLabRegisterUpdaterRole(address _address) external onlyRole(GENETICIST) {
    grantRole(LAB_REGISTER_UPDATER, _address);
  }

  /**
   * @notice Sets a name for a specific creature.
   * @dev Assigns a name to a creature based on its ID. The caller must be the owner of the creature.
   * @param _creatureId The ID of the creature.
   * @param _name The name to assign to the creature.
   */
  function setCreatureName(uint256 _creatureId, string memory _name) public {
    BECCore coreContract = BECCore(payable(coreContractAddress));
    if (msg.sender != coreContract.ownerOf(_creatureId)) revert BECInvalidOwner(msg.sender, _creatureId);
    if (bytes(_name).length > 8 || bytes(_name).length == 0) revert BECInvalidName(_name);
    if (nameUsed[keccak256(abi.encodePacked(_name))]) revert BECDuplicatedName(_name);
    if (!isAZ(bytes(_name))) revert BECInvalidName(_name);

    names[_creatureId] = _name;
    nameUsed[keccak256(abi.encodePacked(_name))] = true;
    familyVariationAndNamesSet[msg.sender] += 1 << 128;

    emit CreatureNameSet(_creatureId, _name);
  }

  /**
   * @notice Retrieves the name of a specific creature.
   * @dev Returns the name of a creature based on its ID. If the creature does not have a custom name, a default name is generated.
   * @param _creatureId The ID of the creature.
   * @return string The name of the creature or a default name if no custom name is set.
   */
  function getCreatureName(uint256 _creatureId) external view virtual returns (string memory) {
    if (bytes(names[_creatureId]).length > 0) {
      return capitalized(names[_creatureId]);
    }
    return string(abi.encodePacked("BEC #", Strings.toString(_creatureId)));
  }
}

File 23 of 35 : BECURIStorage.sol
// contracts/BECURIStorage.sol
// SPDX-License-Identifier: MIT

/**

   /3333333    /33333333    /333333 
  | 33__  33  | 33_____/   /33__  33
  | 33  \ 33  | 33        | 33  \__/
  | 3333333   | 33333     | 33      
  | 33__  33  | 33__/     | 33      
  | 33  \ 33  | 33        | 33    33
  | 3333333/  | 33333333  |  333333/
  |_______/   |________/   \______/ 

 # http://blackeyedcreatures.com

 */

pragma solidity ^0.8.25;

import {ERC404B} from "./erc404/ERC404B.sol";
import {BECAccessControl} from "./BECAccessControl.sol";
import {BECGenesisEngine} from "./BECGenesisEngine.sol";
import {BECWithdrawable} from "./BECWithdrawable.sol";
import {EIP712} from "./eip712/EIP712.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

/**
 * @title Black Eyed Creatures URI Storage
 * @dev Contract for managing URIs and IPFS-related functionality for Black Eyed Creatures (BEC) tokens.
 * @author https://42i.co
 */
abstract contract BECURIStorage is ERC404B, BECAccessControl, BECWithdrawable, EIP712 {

  /// @dev Using Elliptic Curve Digital Signature Algorithm (ECDSA).
  using ECDSA for bytes32;

  /// @dev Base URI for retrieve creature images.
  string _tokenBaseURI;

  /// @dev Base URL for the site.
  string internal _siteBaseUrl;

  /// @dev Genesis engine contract address.
  address genesisEngineContractAddress;

  /// @dev Mapping containing the IPFS uri of each token.
  mapping(uint256 tokenId => string ipfsUri) public ipfsUris;

  // The typehash for the data type specified in the structured data.
  // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#rationale-for-typehash
  bytes32 public constant IPFS_URI_TYPEHASH = keccak256("SetIPFSUri(uint256 creatureId,string uri)");

  /**
   * @dev See {IERC165-supportsInterface}.
   */
  function supportsInterface(bytes4 _interfaceId) public view virtual override(ERC404B, AccessControl) returns (bool) {
    return super.supportsInterface(_interfaceId);
  }

  /**
   * @notice Sets the Base Token URI.
   * @param _uri the URI to set.
   */
  function setBaseURI(string memory _uri) public onlyOwner {
    _tokenBaseURI = _uri;
  }

  /**
   * @notice Sets the Site Base Token URI.
   * @dev Allows the owner to set the base URL for the site.
   * @param _uri The new site base URI to set.
   */
  function setSiteBaseURI(string memory _uri) public onlyOwner {
    _siteBaseUrl = _uri;
  }

  /**
   * @notice Sets the IPFS-specific Token URI.
   * @dev Allows setting a specific IPFS URI for a creature token.
   * @param _creatureId The ID of the creature token.
   * @param _uri The IPFS URI to set for the token.
   * @param _signature The signature to validate the operation.
   */
  function setIPFSTokenURI(uint256 _creatureId, string calldata _uri, bytes calldata _signature) public {
    if (!validateIPFSUriSignature(_creatureId, _uri, _signature)) revert EIP712InvalidSignature(_signature);
    ipfsUris[_creatureId] = _uri;
  }

  /**
   * @notice Gets the Site Base Token URI.
   * @dev Returns the current site base URI.
   * @return uri The current site base URI.
   */
  function getSiteBaseURI() public view returns (string memory uri) {
    return _siteBaseUrl;
  }

  /**
   * @notice Gets the image URI for a creature token.
   * @dev Generates and returns the image URI for a given creature token ID.
   * @param _creatureId The ID of the creature token.
   * @return uri The image URI for the creature token.
   */
  function getImageURI(uint256 _creatureId) public view returns (string memory uri) {
    if (bytes(ipfsUris[_creatureId]).length > 0) {
      return ipfsUris[_creatureId];
    }
    else {
      return string(
        abi.encodePacked(
          _tokenBaseURI,
          Strings.toHexString(uint256(uint160(address(this))), 20),
          "/",
          Strings.toString(_creatureId),
          ".png"
        )
      );
    }
  }

  /**
   * @dev See {IERC721Metadata-tokenURI}.
   */
  function tokenURI(uint256 _creatureId) public view virtual override returns (string memory) {
    BECGenesisEngine genesisEngineContract = BECGenesisEngine(payable(genesisEngineContractAddress));
    return genesisEngineContract.tokenURI(_creatureId);
  }

  /**
   * @notice Sets the address of the Genesis Engine contract.
   * @dev Allows the contract to interact with the Genesis Engine, enabling dynamic token URI generation based on creature DNA.
   * @param _genesisEngineContractAddress The address of the Genesis Engine contract.
   */
  function setGenesisEngineContractAddress(address _genesisEngineContractAddress) public onlyRole(GENETICIST) {
    genesisEngineContractAddress = _genesisEngineContractAddress;
  }

  /**
   * @notice Sets a new signing key for the allowlist.
   * @dev Updates the signing key used to validate signatures for certain privileged operations.
   * @param _signingKey The new public address to be used as the signing key.
   */
  function setAllowlistSigningAddress(address _signingKey) public onlyOwner {
    _setAllowlistSigningAddress(_signingKey);
  }

    /**
   * @notice Validates the signature for setting IPFS image creature uri.
   * @dev Uses EIP-712 signing standard to verify the signature against the expected message format and content,
   *      including the creatureId, and the IPFS URI of the creature.
   * @param _creatureId The creature.
   * @param _uri The IPFS uri for the creature.
   * @param _signature The signature to validate.
   * @return bool True if the signature is valid, false otherwise.
   */
  function validateIPFSUriSignature(
    uint256 _creatureId,
    string calldata _uri,
    bytes calldata _signature
  ) internal view returns (bool) {
    if (allowlistSigningKey == address(0)) revert EIP712BECSigningKeyNotSet();

    bytes32 digest = keccak256(
      abi.encodePacked(
        "\x19\x01",
        EIP712_DOMAIN_SEPARATOR,
        keccak256(abi.encode(IPFS_URI_TYPEHASH, _creatureId, keccak256(bytes(_uri)), msg.sender))
      )
    );

    address recoveredAddress = digest.recover(_signature);
    return recoveredAddress == allowlistSigningKey;
  }
}

File 24 of 35 : BECWithdrawable.sol
// contracts/BECWithdrawable.sol
// SPDX-License-Identifier: MIT

/**

   /3333333    /33333333    /333333 
  | 33__  33  | 33_____/   /33__  33
  | 33  \ 33  | 33        | 33  \__/
  | 3333333   | 33333     | 33      
  | 33__  33  | 33__/     | 33      
  | 33  \ 33  | 33        | 33    33
  | 3333333/  | 33333333  |  333333/
  |_______/   |________/   \______/ 

 # http://blackeyedcreatures.com

 */

pragma solidity ^0.8.25;

import {IBECErrors} from "./IBECErrors.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

/**
 * @title Black Eyed Creatures Widthdrawable Contract
 * @dev This contract adds the availablitily to extract ether and erc-20 token balance from the contract that
 *      implements it.
 * @author https://42i.co
 */
abstract contract BECWithdrawable is Ownable, IBECErrors {

  /// @dev Event emitted when the contract receives ether from an address.
  event Received(address from, uint256 amount);

  /// @dev Constructor for Withdrawable.
  constructor () Ownable(msg.sender) {}

  /**
   * @dev Withdraws specified amount of royalties from contract to address.
   * @param _amount amount of royalties to withdraw.
   * @param _address address to withdraw the balance to.
   */
  function withdraw(uint256 _amount, address payable _address) public payable onlyOwner {
    if (_address == address(0)) revert BECInvalidAddress(_address);
    if (_amount > address(this).balance) revert BECInsufficientFunds(_amount, address(this).balance);
    _address.transfer(_amount);
  }

  /**
   * @dev Withdraws token specified ERC20 amount to the given address.
   * @param _tokenAddress Address of the token to withdraw.
   * @param _amount Amount of tokens to withdraw.
   * @param _address Address to withdraw the tokens to.
   */
  function withdrawERC20Token(
    address _tokenAddress,
    uint256 _amount,
    address payable _address
  ) public payable onlyOwner {
    IERC20 tokenContract = IERC20(_tokenAddress);
    if (_address == address(0)) revert BECInvalidAddress(_address);
    if (_amount > tokenContract.balanceOf(address(this))) revert BECInsufficientFunds(_amount, tokenContract.balanceOf(address(this)));
    tokenContract.transfer(_address, _amount);
  }

  /**
   * @dev Withdraws token specified ERC721 tokenId to the given address.
   * @param _tokenAddress Address of the token to withdraw.
   * @param _tokenId The id of the token to withdraw.
   * @param _address Address to withdraw the tokens to.
   */
  function withdrawERC721Token(
    address _tokenAddress,
    uint256 _tokenId,
    address _address
  ) public payable onlyOwner {
    IERC721 tokenContract = IERC721(_tokenAddress);
    if (_address == address(0)) revert BECInvalidAddress(_address);
    if (address(this) != tokenContract.ownerOf(_tokenId)) revert BECInvalidOwner(address(this), _tokenId);
    tokenContract.safeTransferFrom(address(this), _address, _tokenId);
  }

  /**
   * @dev Function to receive Ether. msg.data must be empty.
   */
  receive() external payable {
    emit Received(msg.sender, msg.value);
  }

  /**
   * @dev Fallback function is called when msg.data is not empty.
   */
  fallback() external payable {}
}

File 25 of 35 : EIP712.sol
// contracts/eip712/EIP712.sol
// SPDX-License-Identifier: MIT

/**

   /3333333    /33333333    /333333 
  | 33__  33  | 33_____/   /33__  33
  | 33  \ 33  | 33        | 33  \__/
  | 3333333   | 33333     | 33      
  | 33__  33  | 33__/     | 33      
  | 33  \ 33  | 33        | 33    33
  | 3333333/  | 33333333  |  333333/
  |_______/   |________/   \______/ 

 # http://blackeyedcreatures.com

 */

pragma solidity ^0.8.25;

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

/**
 * @title EIP712 Base Contract for Typed Structured Data Hashing and Signing
 * @dev Implements EIP-712 standard providing a secure method for generating off-chain signatures 
 *      that can be verified on-chain. Utilizes OpenZeppelin's ECDSA library for cryptographic operations, 
 *      ensuring robust and secure handling of digital signatures.
 * @author https://42i.co
 */
contract EIP712 {

  /**
   * @dev Indicates that the signature was invalid.
   * @param signature The used signature.
   */
  error EIP712InvalidSignature(bytes signature);

  /**
   * @dev Indicates that the signing key was not set.
   */
  error EIP712BECSigningKeyNotSet();

  /// @dev Using Elliptic Curve Digital Signature Algorithm (ECDSA)
  using ECDSA for bytes32;

  /// @dev The key used to sign allowlist signatures.
  address allowlistSigningKey = address(0);

  /// @dev EIP-712 Domain Separator, providing protection against replay attacks across different contracts and networks.
  /// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator
  bytes32 public immutable EIP712_DOMAIN_SEPARATOR;

  /**
   * @dev Initializes the contract by setting up the DOMAIN_SEPARATOR to include the contract's details 
   *      and the current chain ID, ensuring signatures are valid for specific interactions on the intended network.
   */
  constructor() {
    // This should match whats in the client side allowlist signing code
    EIP712_DOMAIN_SEPARATOR = keccak256(
      abi.encode(
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
        keccak256(bytes("BEC")),
        keccak256(bytes("3")),
        block.chainid,
        address(this)
      )
    );
  }

  /**
   * @notice Sets a new signing key for the allowlist, updating the key used for signature validation.
   * @dev Internal function to update the allowlist signing key, ensuring only authorized signatures are considered valid.
   * @param _signingKey The new signing key address to be used for validating signatures.
   */
  function _setAllowlistSigningAddress(address _signingKey) internal {
    allowlistSigningKey = _signingKey;
  }
}

File 26 of 35 : ERC404B.sol
// contracts/erc404/ERC404B.sol
// SPDX-License-Identifier: MIT

/**

    /3333333     /33333333     /333333    
   | 33__  33   | 33_____/    /33__  33   
   | 33  \ 33   | 33         | 33  \__/   
   | 3333333    | 33333      | 33         
   | 33__  33   | 33__/      | 33         
   | 33  \ 33   | 33         | 33    33   
   | 3333333/   | 33333333   |  333333/   
   |_______/    |________/    \______/    

 # https://blackeyedcreatures.com

 */
pragma solidity ^0.8.25;

import {IERC404B} from "./IERC404B.sol";
import {IERC404BErrors} from "./IERC404BErrors.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {IERC20Errors, IERC721Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {DoubleEndedQueue} from "./lib/DoubleEndedQueue.sol";
import {BitMaps} from "solidity-bits/contracts/BitMaps.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";

/**
 * @title Stash Contract
 * @dev This empty contract serves as the designated address for ERC721 tokens that have been minted but not yet burned and currently have no owner.
 */
contract Stash {}


/**
 * @title ERC404B Contract
 * @dev Implements the IERC404 interface to provide a hybrid token mechanism supporting both ERC-20 and ERC-721 functionalities.
 * This contract diverts tokens without current ownership to a "stash" address instead of sending them to the zero address (0x00) as is common with burns.
 * It also enables the actual burning of tokens, sending them irreversibly to the zero address.
 * Features include batch minting for ERC-721 tokens to optimize minting operations, gas savings optimizations, and support for EIP-2612 permit-based transactions.
 * @author https://42i.co
 */
contract ERC404B is IERC404B, IERC20Errors, IERC721Errors, IERC404BErrors {
  using Strings for uint256;
  using BitMaps for BitMaps.BitMap;
  using DoubleEndedQueue for DoubleEndedQueue.Uint256Deque; // DoubleEndedQueue.Uint32Deque would be great

  /// @dev Token Name.
  string private _name;

  /// @dev Token Symbol.
  string private _symbol;

  /// @dev Token decimal digits.
  uint8 private immutable _DECIMALS;

  /// @dev The number of ERC20 tokens required for one ERC721 token.
  uint256 private immutable _UNIT;

  /// @dev The number of tokens held by each address.
  mapping(address holder => uint256 balance) private _balances;

  /// @dev Identifier for the next token to mint.
  uint256 internal _nextTokenId = _startTokenId();

  /// @dev ERC721 token approvals.
  mapping(uint256 tokenId => address approvedAddress) private _tokenApprovals;

  /// @dev ERC721 operator approvals.
  mapping(address holder => mapping(address operator => bool approval)) private _operatorApprovals;

  /// @dev ERC20 token allowances.
  mapping(address holder => mapping(address operator => uint256 allowance)) private _allowances;  

  /// @dev Exempt addresses from ERC-721 transfers (e.g., pairs, routers) for gas savings.
  mapping(address holder => bool exempt) private _erc721TransferExempt;

  /// @dev Bitmask to extract lower 160 bits from a uint256.
  uint256 private constant _BITMASK_LOWER160BITS = (1 << 160) - 1;

  /// @dev Bitmask to extract upper 96 bits from a uint256.
  uint256 private constant _BITMASK_UPPER96BITS = ((1 << 96) - 1) << 160;

  /// @dev Array of owned ERC-721 token IDs, each position stores a batch of tokens with the initial ID (_BITMASK_LOWER160BITS) and quantity (_BITMASK_UPPER96BITS).
  mapping(address holder => uint256[] batchArray) private _owned;

  /// @dev Mapping storing in each position the owner address (_BITMASK_LOWER160BITS) and index of this batch in _owned (_BITMASK_UPPER96BITS).
  mapping(uint256 tokenId => uint256 addressAndPosition) private _ownedData;

  /// @dev Bitmap storing the head of the batches, indicating where the _ownedData is located.
  BitMaps.BitMap private _batchHead;

  /// @dev Bitmap representing burned tokens.
  BitMaps.BitMap private _burnedTokens;

  /// @dev Amount of burned tokens.
  uint256 private _burnedCount = 0;

  /// @dev Queue of ERC-721 tokens in the stash.
  DoubleEndedQueue.Uint256Deque private _stashQueue;

  /// @dev Transfer(address,address,uint256) hash signature.
  bytes32 private constant _TRANSFER_EVENT_SIGNATURE = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

  /// @dev Approval(address,address,uint256) hash signature.
  bytes32 private constant _APPROVAL_EVENT_SIGNATURE = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

  /// @dev ApprovalForAll(address,address,bool) hash signature.
  bytes32 private constant _APPROVALFORALL_EVENT_SIGNATURE = 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;

  /// @dev Stash address for this token.
  address private immutable _STASH_ADDRESS;

  /// @dev EIP-2612 nonces.
  mapping(address => uint256) public nonces;

  /// @dev Initial chain id for EIP-2612 support.
  uint256 internal immutable _INITIAL_CHAIN_ID;

  /// @dev Initial domain separator for EIP-2612 support.
  bytes32 internal immutable _INITIAL_DOMAIN_SEPARATOR;


  /**
   * @dev Initializes the contract with token details and necessary parameters.
   * 
   * @param name_ The name of the token.
   * @param symbol_ The symbol of the token.
   * @param unit_ The equivalence between 1 ERC721 token and ERC20 needed for that token.
   */
  constructor(string memory name_, string memory symbol_, uint256 unit_) {
    _name = name_;
    _symbol = symbol_;

    _DECIMALS = 18;
    _UNIT = unit_ * 10 ** _DECIMALS;
    _STASH_ADDRESS = address(new Stash());

    // EIP-2612 initialization
    _INITIAL_CHAIN_ID = block.chainid;
    _INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
  }

  /**
   * @dev Returns the starting token ID.
   * 
   * @return The starting token ID, which is set to 1 by default.
   * To change the starting token ID, please override this function.
   */
  function _startTokenId() internal pure returns (uint256) {
    return 1;
  }

  /**
   * @dev Returns the total number of tokens minted in the contract.
   * 
   * @return The total number of tokens minted.
   */
  function _totalMinted() internal view virtual returns (uint256) {
    return _nextTokenId - _startTokenId();
  }

  /**
   * @dev See {IERC165-supportsInterface}.
   */
  function supportsInterface(bytes4 interfaceId_) public view virtual returns (bool) {
    return interfaceId_ == type(IERC404B).interfaceId ||
           interfaceId_ == type(IERC165).interfaceId ||
           interfaceId_ == type(IERC20).interfaceId ||
           interfaceId_ == type(IERC721).interfaceId ||
           interfaceId_ == type(IERC721Receiver).interfaceId ||
           interfaceId_ == type(IERC721Metadata).interfaceId;
  }

  /// IERC20 + ERC721 Metadata Methods ///

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

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

  /**
   * @dev See {IERC404-decimals}.
   */
  function decimals() public view virtual override returns (uint8) {
    return _DECIMALS;
  }

  /**
   * @dev See {IERC404-unit}.
   */
  function unit() public view virtual returns (uint256) {
    return _UNIT;
  }

  /**
   * @dev See {IERC404-tokenURI}.
   */
  function tokenURI(uint256 tokenId_) public view virtual override returns (string memory) {
    if (!_exists(tokenId_)) revert ERC721NonexistentToken(tokenId_);
    string memory baseURI = _baseURI();
    return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId_.toString())) : "";
  }

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

  /**
   * @dev Checks whether the specified `tokenId` exists in the contract.
   * Tokens start existing when they are minted using the {_mint} function. Burned tokens are not considered to exist.
   * 
   * @param tokenId_ The ID of the token to check.
   * @return A boolean indicating whether the token exists.
   */
  function _exists(uint256 tokenId_) internal view virtual returns (bool) {
    if (_burnedTokens.get(tokenId_)) {
      return false;
    }
    return tokenId_ < _nextTokenId && _startTokenId() <= tokenId_;
  }

  /**
   * @dev See {IERC404-exists}.
   */
  function exists(uint256 tokenId_) external view virtual returns (bool) {
    return _exists(tokenId_);
  }

  /// ERC721 Methods ///

  /**
   * @dev See {IERC404-ownerOf}.
   */
  function ownerOf(uint256 tokenId_) public view virtual override returns (address owner) {
    if (!_exists(tokenId_)) revert ERC721NonexistentToken(tokenId_);
    uint256 data = _ownedData[_batchHead.scanForward(tokenId_)];
    assembly {
      owner := and(data, _BITMASK_LOWER160BITS)
    }
    if (owner == address(0)) {
      owner = _STASH_ADDRESS;
    }
  }

  /**
   * @dev See {IERC404-safeTransferFrom}.
   */
  function safeTransferFrom(address from_, address to_, uint256 tokenId_) public virtual override {
    safeTransferFrom(from_, to_, tokenId_, "");
  }

  /**
   * @dev See {IERC404-safeTransferFrom}.
   */
  function safeTransferFrom(address from_, address to_, uint256 tokenId_, bytes memory data_) public virtual override {
    if (!_exists(tokenId_)) revert ERC721NonexistentToken(tokenId_);
    if (!_isApprovedOrOwner(msg.sender, tokenId_)) revert ERC721InsufficientApproval(msg.sender, tokenId_);
    _safeTransferERC721(from_, to_, tokenId_, data_);
  }

  /**
   * @dev Safely transfers `tokenId_` token from `from_` to `to_`, ensuring the recipient contract
   * implements the ERC721Receiver interface to prevent tokens from being forever locked.
   *
   * `data_` is additional data without a specified format, included in the call to `to_`.
   *
   * This internal function is equivalent to {safeTransferFrom}, and can be used to implement alternative
   * token transfer mechanisms, such as signature-based ones.
   *
   * Requirements:
   * - `to_` cannot be the zero nor stash address.
   * - `tokenId_` must exist and be owned by `from_`.
   * - If `to_` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}.
   *
   * Emits a {Transfer} event.
   *
   * @param from_ The address sending the token.
   * @param to_ The address receiving the token.
   * @param tokenId_ The ID of the token being transferred.
   * @param data_ Additional data sent during the token transfer.
   */
  function _safeTransferERC721(address from_, address to_, uint256 tokenId_, bytes memory data_) internal virtual {
    if (!_checkOnERC721Received(from_, to_, tokenId_, 1, data_))
      revert ERC721ReceiverNotImplemented(to_, tokenId_, 1, data_);
    if (to_ == address(0) || to_ == _STASH_ADDRESS || to_ == from_) revert ERC721InvalidReceiver(to_);
    _transferERC721(from_, to_, tokenId_);
  }

  /**
   * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
   * The call is not executed if the target address is not a contract.
   *
   * @param from_ Address representing the previous owner of the given token ID.
   * @param to_ Target address that will receive the tokens.
   * @param startTokenId_ The first ID of the tokens to be transferred.
   * @param quantity_ Amount of tokens to be transferred.
   * @param data_ Optional data to send along with the call.
   * @return r Boolean indicating whether the call returned the expected magic value.
   */
  function _checkOnERC721Received(address from_, address to_, uint256 startTokenId_, uint256 quantity_, bytes memory data_) private returns (bool r) {
    if (to_.code.length > 0) {
      r = true;
      for (uint256 tokenId = startTokenId_; tokenId < startTokenId_ + quantity_; tokenId++) {
        try IERC721Receiver(to_).onERC721Received(msg.sender, from_, tokenId, data_) returns (bytes4 retval) {
          r = r && retval == IERC721Receiver.onERC721Received.selector;
        } catch (bytes memory reason) {
          if (reason.length == 0) {
            revert ERC721ReceiverNotImplemented(to_, startTokenId_, quantity_, data_);
          } else {
            assembly {
              revert(add(32, reason), mload(reason))
            }
          }
        }
      }
      return r;
    } else {
      return true;
    }
  }

  /**
   * @dev Returns whether `spender` is allowed to manage `tokenId`.
   *
   * Requirements:
   * - `tokenId` must exist.
   *
   * @param spender_ The address being checked for approval.
   * @param tokenId_ The ID of the token being checked.
   * @return A boolean indicating whether `spender` is allowed to manage `tokenId`.
   */
  function _isApprovedOrOwner(address spender_, uint256 tokenId_) internal view virtual returns (bool) {
    address owner = ownerOf(tokenId_);
    return (spender_ == owner || getApproved(tokenId_) == spender_ || isApprovedForAll(owner, spender_));
  }

  /**
   * @dev Adds a batch of tokens to the `_owned` mapping for the given owner.
   *
   * @param batchInitialId_ The initial ID of the batch of tokens.
   * @param owner_ The address of the owner.
   * @param quantity_ The quantity of tokens in the batch.
   */
  function _pushOwned(uint256 batchInitialId_, address owner_, uint256 quantity_) internal virtual {
    uint256 data;
    assembly {
      data := add(and(batchInitialId_, _BITMASK_LOWER160BITS), and(shl(160, quantity_), _BITMASK_UPPER96BITS))
    }
    _owned[owner_].push(data);
  }

  /**
   * @dev Sets the data for a specific batch of tokens in the `_owned` mapping for the given owner and index.
   *
   * @param batchInitialId_ The initial ID of the batch of tokens.
   * @param owner_ The address of the owner.
   * @param index_ The index of the batch in the `_owned` array.
   * @param quantity_ The quantity of tokens in the batch.
   */
  function _setOwned(uint256 batchInitialId_, address owner_, uint256 index_, uint256 quantity_) internal virtual {
    uint256 data;
    assembly {
      data := add(and(batchInitialId_, _BITMASK_LOWER160BITS), and(shl(160, quantity_), _BITMASK_UPPER96BITS))
    }
    _owned[owner_][index_] = data;
  }

  /**
   * @dev Retrieves the initial ID and quantity of tokens in a batch owned by a specific address.
   *
   * @param owner_ The address of the token owner.
   * @param index_ The index of the batch in the owner's collection.
   * @return batchInitialId The initial ID of the tokens in the batch.
   * @return batchQuantity The quantity of tokens in the batch.
   */
  function _getOwnedBatchInitialIdAndQuantity(address owner_, uint256 index_) internal view virtual returns (uint256 batchInitialId, uint256 batchQuantity) {
    uint256 data = _owned[owner_][index_];
    assembly {
      batchInitialId := and(data, _BITMASK_LOWER160BITS)
      batchQuantity := shr(160, data)
    }
  }

  /**
   * @dev Sets the data for a batch of owned tokens at a specific index in the _ownedData mapping.
   * This function is used to update the data associated with a batch of tokens owned by an address.
   * It ensures that the index does not exceed the upper limit defined by _BITMASK_UPPER96BITS >> 160.
   *
   * @param batchInitialId_ The initial ID of the tokens in the batch.
   * @param ownerOf_ The address of the owner of the tokens in the batch.
   * @param index_ The index of the batch within the _owned[ownerOf_] mapping.
   */
  function _setOwnedData(uint256 batchInitialId_, address ownerOf_, uint256 index_) internal virtual {
    if (index_ > _BITMASK_UPPER96BITS >> 160) {
      revert ERC404OwnedIndexOverflow(index_);
    }
    uint256 data;
    assembly {
      data := add(and(ownerOf_, _BITMASK_LOWER160BITS), and(shl(160, index_), _BITMASK_UPPER96BITS))
    }
    _ownedData[batchInitialId_] = data;
  }

  /**
   * @dev Retrieves the owner, index within the owner's batch, and token ID at the head of the batch for the given token ID.
   *
   * Requirements:
   * - The token ID must exist.
   *
   * @param tokenId_ The ID of the token for which to retrieve information.
   * @return owner The address of the token's owner.
   * @return index The index of the token within the owner's batch.
   * @return tokenIdBatchHead The token ID at the head of the batch.
   */
  function _getOwnerOwnedIndexAndBatchHeadId(uint256 tokenId_) internal view returns (address owner, uint256 index, uint256 tokenIdBatchHead) {
    tokenIdBatchHead = _batchHead.scanForward(tokenId_);
    uint256 data = _ownedData[tokenIdBatchHead];
    assembly {
      index := shr(160, data)
      owner := and(data, _BITMASK_LOWER160BITS)
    }
  }

  /**
   * @dev Transfers an ERC721 token from one address to another.
   *
   * Requirements:
   * - `from_` must be the owner of the token.
   * - Token ID must exist and be owned by `from_`.
   * - If `to_` is a smart contract, it must implement {IERC721Receiver-onERC721Received}.
   * - If `to_` is exempt from ERC721 transfer, the token is moved to the stash.
   *
   * Emits an {IERC721-Transfer} event.
   * Emits an {IERC20-Transfer} event.
   *
   * @param from_ The address transferring the token.
   * @param to_ The address receiving the token.
   * @param tokenId_ The ID of the token being transferred.
   */
  function _transferERC721(address from_, address to_, uint256 tokenId_) internal virtual {
    (address owner, uint256 index, uint256 tokenIdBatchHead) = _getOwnerOwnedIndexAndBatchHeadId(tokenId_);
    // _beforeTokenTransfers(from_, to_, tokenId_, 1);

    delete _tokenApprovals[tokenId_]; // On transfer, any previous approval is reset.

    uint256 batchQuantity;
    uint256 data = _owned[owner][index];
    assembly {
      batchQuantity := shr(160, data)
    }
    _removeTokenFrom(from_, index, tokenId_, tokenIdBatchHead, batchQuantity);

    if (_erc721TransferExempt[to_]) {
      // Is exempt: move to stash
      _batchHead.set(tokenId_);
      _stashQueue.pushFront(tokenId_);

      address mutableStashAddress = _STASH_ADDRESS;
      assembly {
        // Emit ERC721.Transfer(from_, _STASH_ADDRESS, tokenId_)
        log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, and(from_, _BITMASK_LOWER160BITS), and(mutableStashAddress, _BITMASK_LOWER160BITS), tokenId_)
      }
    }
    else {
      // Add ownership to "to_"
      assembly {
        data := add(and(tokenId_, _BITMASK_LOWER160BITS), and(shl(160, 1), _BITMASK_UPPER96BITS))
      }
      _owned[to_].push(data);
      uint256 index_ = _owned[to_].length - 1;
      assembly {
        data := add(and(to_, _BITMASK_LOWER160BITS), and(shl(160, index_), _BITMASK_UPPER96BITS))
      }
      _ownedData[tokenId_] = data;

      assembly {
        // emit IERC721.Transfer(from_, to_, tokenId_);
        log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, and(from_, _BITMASK_LOWER160BITS), and(to_, _BITMASK_LOWER160BITS), tokenId_)
      }
    }

    unchecked {
      // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
      // `from`'s balance is the number of token held, which is at least one before the current
      // transfer.
      // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
      // all 2**256 token ids to be minted, which in practice is impossible.
      _balances[from_] -= _UNIT;
      _balances[to_] += _UNIT;
    }

    data = _UNIT;
    assembly {
      // emit IERC20.Transfer(from_, to_, _UNIT);
      mstore(0x00, data)
      log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, and(from_, _BITMASK_LOWER160BITS), and(to_, _BITMASK_LOWER160BITS))
    }
    // _afterTokenTransfers(from_, to_, tokenId_, 1);
  }

  /**
   * @dev Removes a token from an address, adjusting the internal data structures accordingly.
   *
   * Requirements:
   * - `from_` must be the owner of the token.
   *
   * @param from_ The address from which the token is being removed.
   * @param index_ The index of the token within the owner's batch.
   * @param tokenId_ The ID of the token being removed.
   * @param batchInitialId_ The initial ID of the batch.
   * @param batchQuantity_ The quantity of tokens in the batch.
   */
  function _removeTokenFrom(address from_, uint256 index_, uint256 tokenId_, uint256 batchInitialId_, uint256 batchQuantity_) internal {
    unchecked {
      // If Is Batch Head == No tokens before in the batch.
      if (batchInitialId_ == tokenId_) {
        if (batchQuantity_ == 1) {
          _removeSingleToken(from_, index_);
        } else {
          _removeBatchHeadToken(from_, index_, tokenId_, batchQuantity_);
        }
      } else {
        // Is not batch head == There is tokens before for the
        _removeNonHeadToken(from_, index_, tokenId_, batchInitialId_, batchQuantity_);
      }
    }
  }

  /**
   * @dev Removes a single token from the owner's collection.
   * 
   * This internal function is used during ERC721 token transfers to remove a single token from the owner's collection.
   * It shifts the token data in the owner's collection to fill the gap left by the removed token, ensuring continuous indexing.
   * If the removed token is not the last token in the collection, it updates the metadata and batch information accordingly.
   *
   * @param from_ The address of the owner from which the token is being removed.
   * @param index_ The index of the token in the owner's collection to be removed.
   */
  function _removeSingleToken(address from_, uint256 index_) internal {
    unchecked {
      uint256[] storage ownedFrom = _owned[from_];
      uint256 fromStackLastIndex = ownedFrom.length - 1;
      if (fromStackLastIndex != index_) {
        uint256 data = ownedFrom[fromStackLastIndex];
        ownedFrom[index_] = data;
        uint256 lastBatchInitialId;
        assembly {
          lastBatchInitialId := and(data, _BITMASK_LOWER160BITS)
          data := add(and(from_, _BITMASK_LOWER160BITS), and(shl(160, index_), _BITMASK_UPPER96BITS))
        }
        _ownedData[lastBatchInitialId] = data;
      }
      ownedFrom.pop();
    }
  }

  /**
   * @dev Removes the batch head token from the owner's collection.
   * 
   * This internal function is used during ERC721 token transfers to remove the batch head token from the owner's collection.
   * It sets the subsequent token ID as the new batch head, updates the batch information, and shifts the remaining tokens accordingly.
   *
   * @param from_ The address of the owner from which the batch head token is being removed.
   * @param index_ The index of the batch head token in the owner's collection to be removed.
   * @param tokenId_ The ID of the batch head token being removed.
   * @param batchQuantity_ The quantity of tokens in the batch (including the batch head).
   */
  function _removeBatchHeadToken(address from_, uint256 index_, uint256 tokenId_, uint256 batchQuantity_) internal {
    unchecked {
      uint256 subsequentTokenId = tokenId_ + 1;
      _batchHead.set(subsequentTokenId);

      uint256 data;
      assembly {
        data := add(and(from_, _BITMASK_LOWER160BITS), and(shl(160, index_), _BITMASK_UPPER96BITS))
      }
      _ownedData[subsequentTokenId] = data;

      assembly {
        data := add(
          and(subsequentTokenId, _BITMASK_LOWER160BITS),
          and(shl(160, sub(batchQuantity_, 1)), _BITMASK_UPPER96BITS)
        )
      }
      _owned[from_][index_] = data;
    }
  }

  /**
   * @dev Removes a non-head token from the owner's collection within a batch.
   * 
   * This internal function is used during ERC721 token transfers to remove a token that is not the batch head from the owner's collection within a batch.
   * It updates the batch information, shifts the remaining tokens accordingly, and creates a new batch if necessary.
   *
   * @param from_ The address of the owner from which the token is being removed.
   * @param index_ The index of the token in the owner's collection to be removed.
   * @param tokenId_ The ID of the token being removed.
   * @param batchInitialId_ The ID of the first token in the batch.
   * @param batchQuantity_ The quantity of tokens in the batch.
   */
  function _removeNonHeadToken(address from_, uint256 index_, uint256 tokenId_, uint256 batchInitialId_, uint256 batchQuantity_) internal {
    unchecked {
      _batchHead.set(tokenId_);
      uint256 batchSizeAndIndex = tokenId_ - batchInitialId_;
      uint256 data;
      assembly {
        data := add(and(batchInitialId_, _BITMASK_LOWER160BITS), and(shl(160, batchSizeAndIndex), _BITMASK_UPPER96BITS))
      }
      _owned[from_][index_] = data;

      if (batchSizeAndIndex < batchQuantity_ - 1) {
        // It means that the batch continues
        uint256 subsequentTokenId = tokenId_ + 1;
        _batchHead.set(subsequentTokenId);

        batchSizeAndIndex = (batchQuantity_ - 1) - (tokenId_ - batchInitialId_);
        assembly {
          data := add(and(subsequentTokenId, _BITMASK_LOWER160BITS), and(shl(160, batchSizeAndIndex), _BITMASK_UPPER96BITS))
        }
        _owned[from_].push(data);

        batchSizeAndIndex = _owned[from_].length - 1;
        assembly {
          data := add(and(from_, _BITMASK_LOWER160BITS), and(shl(160, batchSizeAndIndex), _BITMASK_UPPER96BITS))
        }
        _ownedData[subsequentTokenId] = data;
      }
    }
  }

  /**
   * @dev See {IERC404-getApproved}.
   */
  function getApproved(uint256 tokenId_) public view virtual override returns (address) {
    if (!_exists(tokenId_)) revert ERC721NonexistentToken(tokenId_);
    return _tokenApprovals[tokenId_];
  }

  /**
   * @dev See {IERC404-setApprovalForAll}.
   */
  function setApprovalForAll(address operator_, bool approved_) public virtual override {
    address owner = msg.sender;
    if(operator_ == owner) revert ERC721InvalidOperator(operator_);
    _operatorApprovals[owner][operator_] = approved_;
    assembly {
      // emit IERC721.ApprovalForAll(owner, operator, approved);
      mstore(0x00, approved_)
      log3(0x00, 0x20, _APPROVALFORALL_EVENT_SIGNATURE, and(owner, _BITMASK_LOWER160BITS), and(operator_, _BITMASK_LOWER160BITS))
    }
  }

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

  /**
   * @dev Safely mints a specified quantity of ERC721 tokens and assigns them to the specified address.
   *
   * This internal function is equivalent to calling `_safeMint(to, quantity, "")`, providing an empty data parameter.
   * It ensures that the minted tokens are safely transferred to the recipient by calling the ERC721 safe transfer function.
   *
   * @param to_ The address to which the minted tokens will be assigned.
   * @param quantity_ The number of tokens to mint.
   */
  function _safeMint(address to_, uint256 quantity_) internal virtual {
    _safeMint(to_, quantity_, "");
  }

  /**
   * @dev Safely mints a specified quantity of ERC721 tokens and assigns them to the specified address.
   *
   * This internal function is equivalent to calling `_mint(to_, quantity_)` followed by a check to ensure that the recipient contract implements ERC721Receiver.
   * It requires the recipient contract to implement the ERC721Receiver interface's `onERC721Received` function to receive the tokens safely.
   *
   * @param to_ The address to which the minted tokens will be assigned.
   * @param quantity_ The number of tokens to mint.
   * @param data_ Additional data sent along with the token transfer, if any.
   */
  function _safeMint(address to_, uint256 quantity_, bytes memory data_) internal virtual {
    if (!_checkOnERC721Received(address(0), to_, _nextTokenId, quantity_, data_)) {
      revert ERC721ReceiverNotImplemented(to_, _nextTokenId, quantity_, data_);
    }
    _mint(to_, quantity_);
  }


  /**
   * @dev Mints a specified quantity of ERC721 tokens and assigns them to the specified address.
   *
   * This internal function performs the minting of ERC721 tokens and updates the balances accordingly. 
   * It also emits one ERC20 Transfer event and a ERC721 Transfer event for each minted token.
   * If the recipient address is exempt from ERC721 transfer fees, the method sets the batch head accordingly; 
   * otherwise, it adds the minted tokens to the recipient's ownership.
   *
   * Requirements:
   * - `quantity_` must be greater than 0.
   * - `to_` address must not be the zero nor stash address.
   *
   * Emits an IERC20 {Transfer} event.
   * Emits an IERC721 {Transfer} event for each minted token.
   *
   * @param to_ The address to which the minted tokens will be assigned.
   * @param quantity_ The number of tokens to mint.
   */
  function _mint(address to_, uint256 quantity_) internal virtual {
    if (quantity_ == 0) revert ERC721InvalidMintQuantity();
    if (to_ == address(0) || to_ == _STASH_ADDRESS) revert ERC20InvalidReceiver(to_);
    // _beforeTokenTransfers(address(0), to_, _nextTokenId, quantity_);

    uint256 value;
    uint256 toMasked;
    uint256 end;
    uint256 batchTokenId = _nextTokenId;
    unchecked {
      value = quantity_ * _UNIT;
      _balances[to_] += value; // May overflow in big quantities if total balance not controlled. 
      end = batchTokenId + quantity_;
    }

    assembly {
      // emit IERC20.Transfer(0x00, to_, _UNIT);
      toMasked := and(to_, _BITMASK_LOWER160BITS)
      mstore(0x00, value)
      log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, 0x00, toMasked)
    }

    if (_erc721TransferExempt[to_]) {
      _batchHead.setBatch(batchTokenId, quantity_);
    } else {
      _batchHead.set(batchTokenId);
      _pushOwned(batchTokenId, to_, quantity_);
      _setOwnedData(batchTokenId, to_, _owned[to_].length - 1);

      // Use assembly to loop and emit the `Transfer` event for gas savings.
      // The duplicated `log4` removes an extra check and reduces stack juggling.
      assembly {
        // emit IERC721.Transfer(0x00, to_, batchTokenId);
        log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, 0x00, toMasked, batchTokenId)

        // The `iszero(eq(,))` check ensures that large values of `quantity`
        // that overflows uint256 will make the loop run out of gas.
        // The compiler will optimize the `iszero` away for performance.
        for {
          let tokenId := add(batchTokenId, 1)
        } iszero(eq(tokenId, end)) {
          tokenId := add(tokenId, 1)
        } {
          // emit IERC721.Transfer(0x00, to_, tokenId);
          log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, 0x00, toMasked, tokenId)
        }
      }
    }

    // _afterTokenTransfers(address(0), to_, _nextTokenId, quantity_);
    unchecked {
      _nextTokenId += quantity_;
    }
  }

  /// ERC20 Methods ///

  /**
   * @dev See {IERC404-totalSupply}.
   */
  function totalSupply() public view virtual override returns (uint256) {
    return _totalTokens() * _UNIT;
  }

  /**
   * @dev Calculates the total number of tokens that have been minted and not burned.
   *
   * This internal function returns the difference between the total minted tokens and the burned tokens, representing the total number of existing tokens in circulation.
   *
   * @return The total number of existing tokens in circulation.
   */
  function _totalTokens() internal view virtual returns (uint256) {
    return _totalMinted() - _burned();
  }

  /**
   * @dev Retrieves the total number of tokens that have been burned.
   *
   * This internal function returns the count of tokens that have been burned within the contract.
   *
   * @return The total number of tokens that have been burned.
   */
  function _burned() internal view virtual returns (uint256) {
    return _burnedCount;
  }

  /**
   * @dev See {IERC404-transfer}.
   */
  function transfer(address to_, uint256 valueOrId_) public virtual override returns (bool) {
    address owner = msg.sender;
    transferFrom(owner, to_, valueOrId_); 
    return true;
  }

  /**
   * @dev See {IERC404-allowance}.
   */
  function allowance(address owner_, address spender_) public view virtual override returns (uint256) {
    return _allowances[owner_][spender_];
  }

  /// ERC404 Combined (Methods with similar interfaces and behavior in ERC20 & ERC721) ///

  /**
   * @dev See {IERC404-balanceOf}.
   */
  function balanceOf(address account_) public view virtual override returns (uint256) {
    return _balances[account_];
  }

  /**
   * @dev See {IERC404-transferFrom}.
   */
  function transferFrom(address from_, address to_, uint256 valueOrId_) public virtual returns (bool) {
    if (_exists(valueOrId_)) {
      safeTransferFrom(from_, to_, valueOrId_, "");
    } else {
      return _transferFromERC20(from_, to_, valueOrId_);
    }
    return true;
  }

  /**
   * @dev Transfers ERC20 tokens from one address to another, handling ERC721 exemptions internally.
   *
   * This function is used to transfer ERC20 tokens directly between addresses, handling exemptions for ERC721 transfers
   * internally. It checks for valid recipients, allowances, and handles ERC721 exemptions if the recipient address is
   * exempt from ERC721 transfers.
   *
   * Requirements:
   * - `to_` cannot be the zero address or the stash address.
   *
   * @param from_ The address sending the ERC20 tokens.
   * @param to_ The address receiving the ERC20 tokens.
   * @param value_ The amount of ERC20 tokens to transfer.
   * @return A boolean indicating whether the transfer was successful.
   */
  function _transferFromERC20(address from_, address to_, uint256 value_) public virtual returns (bool) {
    if (to_ == address(0) || to_ == _STASH_ADDRESS || to_ == from_) revert ERC20InvalidReceiver(to_);

    if (from_ != msg.sender) {
      uint256 allowed = _allowances[from_][msg.sender];

      // Check that the operator has sufficient allowance.
      if (allowed != type(uint256).max) {
        if(value_ > allowed) revert ERC20InsufficientAllowance(from_, allowed, value_);
        _allowances[from_][msg.sender] = allowed - value_;
      }
    }

    // Transferring ERC-20s directly requires the _transferERC20WithERC721 function.
    return _transferERC20WithERC721(from_, to_, value_);
  }

  /**
   * @dev This function handles the transfer of ERC-20 tokens and optionally adjusts the ERC-721 token balances based on the transfer exemptions.
   * 
   * Requirements:
   * - The sender (`from_`) must have a balance of at least `value_`.
   * 
   * Emits:
   * - {IERC20.Transfer} event for ERC-20 and possibly a {IERC721.Transfer} for each ERC-721 Token.
   * 
   * @param from_ Address sending the tokens.
   * @param to_ Address receiving the tokens.
   * @param value_ Amount of ERC-20 tokens to transfer.
   * @return bool True if the transfer is successful.
   */
  function _transferERC20WithERC721(address from_, address to_, uint256 value_) internal virtual returns (bool) {
    uint256 erc20BalanceOfSenderBefore = _balances[from_];
    uint256 erc20BalanceOfReceiverBefore = _balances[to_];

    if (erc20BalanceOfSenderBefore < value_) revert ERC20InsufficientBalance(from_, erc20BalanceOfSenderBefore, value_);

    unchecked {
      _balances[from_] -= value_;
      _balances[to_] += value_;
    }

    assembly {
      // emit IERC20.Transfer(from_, to_, value_);
      mstore(0x00, value_)
      log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, and(from_, _BITMASK_LOWER160BITS), and(to_, _BITMASK_LOWER160BITS))
    }

    // Skip ERC-721 transfer to exempt addresses to save gas
    bool isFromERC721TransferExempt = _erc721TransferExempt[from_];
    bool isToERC721TransferExempt = _erc721TransferExempt[to_];
    if (isFromERC721TransferExempt && isToERC721TransferExempt) {
      // Case 1) Both sender and recipient are ERC-721 transfer exempt. No ERC-721s need to be transferred.
    } else if (isFromERC721TransferExempt) {
      // Case 2) The sender is ERC-721 transfer exempt, but the recipient is not. 
      unchecked {
        uint256 tokensToRetrieveFromStash = (_balances[to_] / _UNIT) - (erc20BalanceOfReceiverBefore / _UNIT);
        _retrieveFromStash(to_, tokensToRetrieveFromStash);
      }
    } else if (isToERC721TransferExempt) {
      // Case 3) The sender is not ERC-721 transfer exempt, but the recipient is. 
      unchecked {
        uint256 tokensToStash = (erc20BalanceOfSenderBefore / _UNIT) - (_balances[from_] / _UNIT);
        _stash(from_, tokensToStash);
      }
    } else {
      // Case 4) Neither the sender nor the recipient are ERC-721 transfer exempt.
      _batchTransferERC721WithBalanceAdjustment(from_, to_, erc20BalanceOfSenderBefore, erc20BalanceOfReceiverBefore, value_);
    }
    return true;
  }

  /**
   * @dev Internal function to batch transfer ERC721 tokens with balance adjustment.
   * 
   * Emits a {IERC721.Transfer} event for each token transferred, including the initial token ID and all subsequent IDs in the batch.
   *
   * @param from_ The address from which to transfer tokens.
   * @param to_ The address to which to transfer tokens.
   * @param fromPreviousBalance_ The previous balance of tokens for the sender.
   * @param toPreviousBalance_ The previous balance of tokens for the recipient.
   * @param transferedValue_ The value of tokens to be transferred.
   */
  function _batchTransferERC721WithBalanceAdjustment(address from_, address to_, uint256 fromPreviousBalance_, uint256 toPreviousBalance_, uint256 transferedValue_) internal {
    uint256 tokenId;
    uint256 end;
    uint256 nftsToTransfer = transferedValue_ / _UNIT;
    for (uint256 i = 0; i < nftsToTransfer; ) {
      // Transfers the whole batch
      uint256 lastOwnedIndex = _owned[from_].length - 1;
      (uint256 batchInitialId_, uint256 batchQuantity_) = _getOwnedBatchInitialIdAndQuantity(from_, lastOwnedIndex);

      if (batchQuantity_ + i <= nftsToTransfer) {
        // Transfer whole batch
        _owned[to_].push(_owned[from_][lastOwnedIndex]);
        _owned[from_].pop();

        uint256 lastToIndex = _owned[to_].length - 1;
        _setOwnedData(batchInitialId_, to_, lastToIndex);

        unchecked {
          tokenId = batchInitialId_;
          end = batchInitialId_ + batchQuantity_;
          i += batchQuantity_;
        }
      } else {
        // Transfers part of the batch
        unchecked {
          uint256 tokensToTransfer = nftsToTransfer - i;
          uint256 tokensInPreviousBatch = batchQuantity_ - tokensToTransfer;
          _setOwned(batchInitialId_, from_, lastOwnedIndex, tokensInPreviousBatch);

          uint256 newBatchInitialId = batchInitialId_ + tokensInPreviousBatch;
          _batchHead.set(newBatchInitialId);
          _pushOwned(newBatchInitialId, to_, tokensToTransfer);
          _setOwnedData(newBatchInitialId, to_, _owned[to_].length - 1);

          tokenId = newBatchInitialId;
          end = newBatchInitialId + tokensToTransfer;
          i = nftsToTransfer;
        }
      }
      unchecked {
        for (uint256 j = tokenId; j < end; ++j) {
          delete _tokenApprovals[j]; // On transfer, any previous approval is reset.

          assembly {
            // emit IERC721.Transfer(from_, to_, emitInitialId);
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, and(from_, _BITMASK_LOWER160BITS), and(to_, _BITMASK_LOWER160BITS), j)
          }
        }
      }
    }

    // If the transfer changes either the sender or the recipient's holdings from a fractional to a non-fractional
    // amount (or vice versa), adjust ERC-721s.
    unchecked {
      // First check if the send causes the sender to lose a whole token that was represented by an ERC-721
      // due to a fractional part being transferred.
      if (fromPreviousBalance_ / _UNIT - _balances[from_] / _UNIT > nftsToTransfer) {
        _stash(from_, 1);
      }

      // Then, check if the transfer causes the receiver to gain a whole new token which requires gaining
      // an additional ERC-721.
      if (_balances[to_] / _UNIT - toPreviousBalance_ / _UNIT > nftsToTransfer) {
        _retrieveFromStash(to_);
      }
    }
  }

  /**
   * @dev Internal virtual function to stash ERC721 tokens.
   * 
   * Emits a {IERC721.Transfer} event for each token stashed.
   *
   * @param from_ The address from which to stash tokens.
   * @param quantity_ The quantity of tokens to be stashed.
   */
  function _stash(address from_, uint256 quantity_) internal virtual {
    unchecked {
      uint256 batchInitialId_;
      uint256 batchQuantity_;
      uint256 data;
      // Stash loop variables
      uint256 begin;
      uint256 end;

      for (uint256 stashed = 0; stashed < quantity_; ) {
        data = _owned[from_][_owned[from_].length - 1];
        assembly {
          batchInitialId_ := and(data, _BITMASK_LOWER160BITS)
          batchQuantity_ := shr(160, data)
        }
        if (stashed + batchQuantity_ <= quantity_) {
          // Transfer the whole batch
          delete _ownedData[batchInitialId_];
          _batchHead.setBatch(batchInitialId_, batchQuantity_); // Set batchead in a massive way to all tokens.
          _owned[from_].pop(); // Remove batch from owned ids
          stashed += batchQuantity_; // Increment the stashed items
          begin = batchInitialId_; 
          end = begin + batchQuantity_;
        } else {
          // Only transfer the amount needed, maintain the batch
          uint256 tokensToStash = quantity_ - stashed;
          uint256 nonStashedBatchSize = batchQuantity_ - tokensToStash;
          begin = batchInitialId_ + nonStashedBatchSize;
          end = begin + tokensToStash;
          _batchHead.setBatch(begin, tokensToStash); // Set batchead in a massive way to all tokens to be stashed
          _setOwned(batchInitialId_, from_, _owned[from_].length - 1, nonStashedBatchSize); // Update the batch size
          stashed = quantity_; // Update the stashed items
        }
        address mutableStashAddress = _STASH_ADDRESS;
        for (uint256 i=begin; i<end; ++i) {
          _stashQueue.pushFront(i);
          delete _tokenApprovals[i]; // On stash of a token, any previous approval is reset.
          assembly {
            // emit IERC721.Transfer(from_, _STASH_ADDRESS, tokenId);
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, and(from_, _BITMASK_LOWER160BITS), and(mutableStashAddress, _BITMASK_LOWER160BITS), i)
          }
        }
      }
    }
  }

  /**
   * @dev Internal virtual function to retrieve ERC721 tokens from the stash.
   * 
   * Emits a {IERC721.Transfer} event for each token retrieved.
   *
   * @param to_ The address to which retrieved tokens will be transferred.
   */
  function _retrieveFromStash(address to_) internal virtual {
    uint256 id = _stashQueue.popBack();
    _pushOwned(id, to_, 1);
    unchecked {
      _setOwnedData(id, to_, _owned[to_].length - 1);
    }

    address mutableStashAddress = _STASH_ADDRESS;
    assembly {
      // emit IERC721.Transfer(_STASH_ADDRESS, to_, tokenId);
      log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, and(mutableStashAddress, _BITMASK_LOWER160BITS), and(to_, _BITMASK_LOWER160BITS), id)
    }
  }

  /**
   * @dev Internal function to retrieve multiple ERC721 tokens from the stash and transfer them to a recipient.
   * 
   * Emits a {IERC721.Transfer} event for each token retrieved, including the token ID transferred to the recipient.
   * 
   * @param to_ The address to which retrieved tokens will be transferred.
   * @param amount_ The number of tokens to retrieve from the stash.
   */
  function _retrieveFromStash(address to_, uint256 amount_) internal {
    for (uint256 i = 0; i < amount_; ) {
      _retrieveFromStash(to_);
      unchecked {
        ++i;
      }
    }
  }

  /**
   * @dev See {IERC404-approve}.
   */
  function approve(address spender_, uint256 valueOrId_) public virtual returns (bool) {
    if (_exists(valueOrId_)) {
      _erc721Approve(spender_, valueOrId_);
    } else {
      return _erc20Approve(spender_, valueOrId_);
    }
    return true;
  }

  /**
   * @dev Approves a specific address to transfer the specified ERC-721 token.
   * 
   * Requirements:
   * - The caller must be the owner of the ERC-721 token or have been approved by the owner.
   * 
   * Emits:
   * - {IERC721.Approval} event for ERC-721 Tokens.
   * 
   * @param spender_ Address to be approved for the specified ERC-721 token.
   * @param id_ ID of the ERC-721 token to be approved.
   */
  function _erc721Approve(address spender_, uint256 id_) public virtual {
    address erc721Owner = ownerOf(id_);
    if (msg.sender != erc721Owner && !isApprovedForAll(erc721Owner, msg.sender)) {
      revert ERC721InvalidApprover(msg.sender);
    }
    _tokenApprovals[id_] = spender_;

    assembly {
      // emit IERC721.Approval(erc721Owner, spender_, id_);
      log4(0x00, 0x00, _APPROVAL_EVENT_SIGNATURE, and(erc721Owner, _BITMASK_LOWER160BITS), and(spender_, _BITMASK_LOWER160BITS), id_)
    }
  }

  /**
   * @dev Approves a specific address to spend a specified amount of ERC-20 tokens on behalf of the caller.
   * 
   * Requirements:
   * - The spender address must not be the zero address.
   * 
   * Emits:
   * - {IERC20.Approval} event for ERC-20 Tokens.
   * 
   * @param spender_ Address to be approved for spending the specified ERC-20 tokens.
   * @param value_ Amount of ERC-20 tokens to be approved for spending.
   * @return bool True if the approval is successful.
   */
  function _erc20Approve(address spender_, uint256 value_) public virtual returns (bool) {
    address owner = msg.sender;
    if (spender_ == address(0) || spender_ == _STASH_ADDRESS) {
      revert ERC20InvalidSpender(spender_);
    }
    _allowances[owner][spender_] = value_;

    assembly {
      // emit IERC20.Approval(msg.sender, spender_, value_);
      let ownerMasked := and(owner, _BITMASK_LOWER160BITS)
      let approvedMasked := and(spender_, _BITMASK_LOWER160BITS)
      mstore(0x00, value_)
      log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, ownerMasked, approvedMasked)
    }
    return true;
  }

  /**
   * @dev See {IERC20Permit-permit}. 
   */
  function permit(address owner_, address spender_, uint256 value_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_) public virtual {
    if (deadline_ < block.timestamp) {
      revert EIP2612PermitDeadlineExpired(owner_, spender_, value_, deadline_, v_, r_, s_);
    }

    // permit cannot be used for ERC-721 token approvals, so ensure
    // the value does not fall within the valid range of ERC-721 token ids.
    if (_exists(value_)) {
      revert ERC404InvalidTransferValue(value_);
    }

    if (spender_ == address(0) || spender_ == _STASH_ADDRESS) {
      revert ERC20InvalidSpender(spender_);
    }

    unchecked {
      address recoveredAddress = ecrecover(
        keccak256(
          abi.encodePacked(
            "\x19\x01",
            DOMAIN_SEPARATOR(),
            keccak256(
              abi.encode(
                keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),
                owner_,
                spender_,
                value_,
                nonces[owner_]++,
                deadline_
              )
            )
          )
        ),
        v_,
        r_,
        s_
      );

      if (recoveredAddress == address(0) || recoveredAddress != owner_) {
        revert EIP2612InvalidSigner(recoveredAddress, owner_, spender_, value_, deadline_, v_, r_, s_);
      }

      _allowances[recoveredAddress][spender_] = value_;
    }

    assembly {
      // emit IERC20.Approval(owner_, spender_, value_);
      let ownerMasked := and(owner_, _BITMASK_LOWER160BITS)
      let approvedMasked := and(spender_, _BITMASK_LOWER160BITS)
      mstore(0x00, value_)
      log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, ownerMasked, approvedMasked)
    }
  }

  /**
   * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. 
   */
  function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
    return block.chainid == _INITIAL_CHAIN_ID ? _INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator();
  }

  /**
   * @notice Internal function to compute the domain separator for EIP-2612 permits.
   * @dev This function computes the domain separator based on the contract's name, version, chain ID, and address.
   * 
   * @return bytes32 The computed domain separator value.
   */
  function _computeDomainSeparator() internal view virtual returns (bytes32) {
    return
      keccak256(
        abi.encode(
          keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
          keccak256(bytes(_name)),
          keccak256("1"),
          block.chainid,
          address(this)
        )
      );
  }

  /**
   * @dev See {IERC404-isERC721TransferExempt}. 
   */
  function isERC721TransferExempt(address target_) external view override returns (bool isExempt) {
    isExempt = _erc721TransferExempt[target_];
  }

  /**
   * @dev See {IERC404-setERC721TransferExempt}. 
   */
  function setERC721TransferExempt(bool state_) external override {
    _setERC721TransferExempt(msg.sender, state_);
  }

  /**
   * @dev Internal function to set the exemption status for ERC-721 transfers.
   * 
   * Requirements:
   * - `target_` address cannot be the zero address or the stash address.
   * 
   * @param target_ The address for which to set the exemption status.
   * @param state_ The new exemption state to set (true for exempt, false for non-exempt).
   */
  function _setERC721TransferExempt(address target_, bool state_) internal virtual {
    if (target_ == address(0) || target_ == _STASH_ADDRESS) {
      revert ERC404InvalidERC721Exemption(target_);
    }

    // Adjust the ERC721 balances of the target to respect exemption rules.
    if (state_) {
      _stashAll(target_);
    } else {
      _retrieveAllFromStash(target_);
    }

    _erc721TransferExempt[target_] = state_;
  }

  /**
   * @dev Internal function to stash all ERC-721 tokens owned by the target address.
   * 
   * Emits:
   * - {IERC721.Transfer} event for ERC-721 tokens being transferred to the stash.
   * 
   * @param target_ The address whose tokens are to be stashed.
   */
  function _stashAll(address target_) private {
    uint256[] memory ownedTarget = _owned[target_];
    for (uint256 i = 0; i < ownedTarget.length; ) {
      (uint256 batchInitialId_, uint256 batchQuantity_) = _getOwnedBatchInitialIdAndQuantity(target_, i);
      delete _ownedData[batchInitialId_]; // Resets _ownedData
      _batchHead.setBatch(batchInitialId_, batchQuantity_); // Set batchead in a massive way to all tokens.

      // add all tokens to the stash
      unchecked {
        uint256 end = batchInitialId_ + batchQuantity_;
        address mutableStashAddress = _STASH_ADDRESS;

        for (uint256 b = batchInitialId_; b < end; ++b) {
          delete _tokenApprovals[b]; // On stash of a token, any previous approval is reset.
          _stashQueue.pushFront(b);
          assembly {
            // emit IERC721.Transfer(target_, _STASH_ADDRESS, batchInitialId_);
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, and(target_, _BITMASK_LOWER160BITS), and(mutableStashAddress, _BITMASK_LOWER160BITS), b)
          }
        }
        ++i;
      }
    }
    delete _owned[target_];
  }

  /**
   * @dev Internal function to retrieve all ERC-721 tokens from the stash for the target address.
   * 
   * Emits:
   * - {IERC721.Transfer} event for ERC-721 tokens being transferred from the stash.
   * 
   * @param target_ The address to retrieve ERC-721 tokens for.
   */
  function _retrieveAllFromStash(address target_) private {
    uint256 expectedERC721Balance = _balances[target_] / _UNIT;
    uint256 actualERC721Balance = 0;
    uint256[] memory ownedTarget = _owned[target_];
    for (uint256 i = 0; i < ownedTarget.length; ) {
      uint256 data = ownedTarget[i];
      assembly {
        actualERC721Balance := add(actualERC721Balance, shr(160, data))
        i := add(i, 1) // Avoiding an unchecked block after this one
      }
    }

    unchecked {
      expectedERC721Balance -= actualERC721Balance;
      for (uint256 i = 0; i < expectedERC721Balance; ++i) {
        // Transfer ERC721 balance in from pool
        _retrieveFromStash(target_);
      }
    }
  }

  /**
   * @dev See {IERC404-owned}. 
   */
  function owned(address owner_) public view virtual returns (uint256[] memory ownedCreatureIds) {
    if (owner_ == _STASH_ADDRESS) return tokensInStash();
    uint256 size = 0;
    uint256 data;
    uint256[] memory ownedOwner = _owned[owner_];
    for (uint256 i = 0; i < ownedOwner.length; ) {
      data = ownedOwner[i];
      assembly {
        size := add(size, shr(160, data))
        i := add(i, 1)
      }
    }
    ownedCreatureIds = new uint256[](size);

    unchecked {
      uint256 ix = 0;
      uint256 batchInitialId_;
      uint256 batchQuantity_;
      for (uint256 i = 0; i < ownedOwner.length; ++i) {
        data = ownedOwner[i];
        assembly {
          batchInitialId_ := and(data, _BITMASK_LOWER160BITS)
          batchQuantity_ := shr(160, data)
        }

        for (uint256 j = 0; j < batchQuantity_; ++j) {
          ownedCreatureIds[ix] = batchInitialId_ + j;
          ++ix;
        }
      }
    }
  }

  /**
   * @dev Internal function to burn (destroy) an ERC-721 token.
   * 
   * Emits:
   * - {IERC721.Transfer} event from `from` to `address(0)` (burning the token).
   * - {IERC20.Transfer} event from `from` to `address(0)` with `_UNIT` value (The ERC-20 side of the token).
   * 
   * @param tokenId_ ID of the ERC-721 token to be burned.
   */
  function _burn(uint256 tokenId_) internal virtual {
    if (!_exists(tokenId_)) revert ERC721NonexistentToken(tokenId_);
    (address from, uint256 index, uint256 tokenIdBatchHead) = _getOwnerOwnedIndexAndBatchHeadId(tokenId_);
    // _beforeTokenTransfers(from, to, tokenId, 1);

    delete _tokenApprovals[tokenId_]; // On transfer, any previous approval is reset.

    uint256 batchQuantity_;
    uint256 data = _owned[from][index];
    assembly {
      batchQuantity_ := shr(160, data)
    }

    _removeTokenFrom(from, index, tokenId_, tokenIdBatchHead, batchQuantity_);
    delete _ownedData[tokenId_];
    _batchHead.set(tokenId_);
    _burnedTokens.set(tokenId_);

    unchecked {
      // Cannot overflow, as that would require more tokens to be burned/transferred
      // out than the owner initially received through minting and transferring in.
      _balances[from] -= _UNIT;
      ++_burnedCount;
    }

    data = _UNIT;
    assembly {
      let fromMasked := and(from, _BITMASK_LOWER160BITS)
      // emit IERC20.Transfer(from_, to_, _UNIT);
      mstore(0x00, data)
      log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, fromMasked, 0x00)

      // emit IERC721.Transfer(from, address(0), tokenId_);
      log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, fromMasked, 0x00, tokenId_)
    }
    // _afterTokenTransfers(from, address(0), tokenId, 1);
  }

  /**
   * @dev See {IERC404-stashAddress}. 
   */
  function stashAddress() public view returns (address) {
    return _STASH_ADDRESS;
  }

  /**
   * @dev See {IERC404-stashLength}. 
   */
  function stashLength() public view returns (uint256) {
    return _stashQueue.length();
  }

  /**
   * @dev See {IERC404-tokensInStash}. 
   */
  function tokensInStash() public view returns (uint256[] memory) {
    return _stashQueue.retriveQueueItems();
  }

  /**
   * @dev See {IERC404-exchangeWithStash}. 
   */
  function exchangeWithStash(uint256 tokenId_, uint128 index_) public {
    uint256 stashTokenId = _stashQueue.at(index_);
    if (!_exists(tokenId_)) revert ERC721NonexistentToken(tokenId_);
    if (!_isApprovedOrOwner(msg.sender, tokenId_)) revert ERC721InsufficientApproval(msg.sender, tokenId_);
    (address owner, uint256 index, uint256 tokenIdBatchHead) = _getOwnerOwnedIndexAndBatchHeadId(tokenId_);
    // _beforeTokenTransfers(msg.sender, _STASH_ADDRESS, tokenId_, 1);
    // _beforeTokenTransfers(_STASH_ADDRESS, msg.sender, stashTokenId, 1);

    delete _tokenApprovals[tokenId_]; // On transfer, any previous approval is reset.

    uint256 batchQuantity_;
    uint256 data = _owned[owner][index];
    assembly {
      batchQuantity_ := shr(160, data)
    }
    _removeTokenFrom(owner, index, tokenId_, tokenIdBatchHead, batchQuantity_);
    delete _ownedData[tokenId_];
    _batchHead.set(tokenId_);
    _stashQueue.setIndexValue(index_, tokenId_); // Sets the token to exchange into the stash

    // Now, sets the Stash token to the tokenId owner
    assembly {
      data := add(and(stashTokenId, _BITMASK_LOWER160BITS), and(shl(160, 1), _BITMASK_UPPER96BITS))
    }
    _owned[owner].push(data);
    unchecked {
      uint256 ownedIndex = _owned[owner].length - 1;
      assembly {
        data := add(and(owner, _BITMASK_LOWER160BITS), and(shl(160, ownedIndex), _BITMASK_UPPER96BITS))
      }
    }
    _ownedData[stashTokenId] = data;

    address stashMasked = _STASH_ADDRESS;
    assembly {
      stashMasked := and(stashMasked, _BITMASK_LOWER160BITS)
      let ownerMasked := and(owner, _BITMASK_LOWER160BITS)

      // emit IERC721.Transfer(_STASH_ADDRESS, owner, stashTokenId);
      log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, stashMasked, ownerMasked, stashTokenId)
      // emit IERC721.Transfer(owner, _STASH_ADDRESS, tokenId_);
      log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, ownerMasked, stashMasked, tokenId_)
    }
    // _afterTokenTransfers(msg.sender, _STASH_ADDRESS, tokenId_, 1);
    // _afterTokenTransfers(_STASH_ADDRESS, msg.sender, stashTokenId, 1);
  }
}

File 27 of 35 : IERC404B.sol
// contracts/erc404/IERC404B.sol
// SPDX-License-Identifier: MIT

/**

    /3333333     /33333333     /333333    
   | 33__  33   | 33_____/    /33__  33   
   | 33  \ 33   | 33         | 33  \__/   
   | 3333333    | 33333      | 33         
   | 33__  33   | 33__/      | 33         
   | 33  \ 33   | 33         | 33    33   
   | 3333333/   | 33333333   |  333333/   
   |_______/    |________/    \______/    

 # https://blackeyedcreatures.com

 */
pragma solidity ^0.8.25;

import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

/**
 * @title IERC404B Interface
 * @dev Interface for a hybrid token contract combining ERC20 and ERC721 functionalities with a unique "stash" feature.
 * The stash is a holding area for tokens that are currently unowned but not burned, allowing for controlled management and re-distribution.
 * Supports ERC165 for interface detection and ERC20Permit for token allowance via signatures.
 */
interface IERC404B is IERC165, IERC20Permit {
 
  /// IERC20 + ERC721 Metadata Methods ///

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

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

  /**
   * @dev Returns the number of decimals used to get its user representation.
   * For example, if `decimals` equals `2`, a balance of `505` tokens should
   * be displayed to a user as `5.05` (`505 / 10 ** 2`).
   *
   * Tokens usually opt for a value of 18, imitating the relationship between
   * Ether and Wei. This is the default value returned by this function, unless
   * it's overridden.
   *
   * NOTE: This information is only used for _display_ purposes: it in
   * no way affects any of the arithmetic of the contract, including
   * {IERC20-balanceOf} and {IERC20-transfer}.
   */
  function decimals() external view returns (uint8);

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


  /// ERC721 Methods ///

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

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

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

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

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

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

  /// ERC20 Methods ///

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

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

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

  /// ERC404 Combined (Methods with similar interfaces and behavior in ERC20 & ERC721) ///

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

  /**
   * @dev Moves `amountOrId` tokens from `from` to `to` using the
   * allowance mechanism. `amountOrId` is then deducted from the caller's
   * allowance.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * WARNING: In case of Id, note that the caller is responsible to confirm that the recipient is capable of
   * receiving ERC721 or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though
   * the caller must understand this adds an external call which potentially creates a reentrancy vulnerability.
   *
   * Requirements:
   * - `from` cannot be the zero address.
   * - `to` cannot be the zero address.
   * - `amountOrId` amount should be less or equal than balance OR tokenId 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 {Transfer} events.
   */
  function transferFrom(address from, address to, uint256 amountOrId) external returns (bool);

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


  /// ERC404 Specific ///

 /**
   * @notice Retrieves the equivalence between 1 ERC721 token and ERC20 needed for that token.
   * @dev This function returns the unit value used in conversions between ERC721 and ERC20 tokens.
   * @return The unit value representing the equivalence between 1 ERC721 token and ERC20.
   */
  function unit() external view returns (uint256);

    /**
   * @notice Checks if the specified token exists.
   *
   * A token is considered to exist if it has been minted using {_mint} and is not in the set of burned tokens.
   *
   * @param tokenId_ The ID of the token to check.
   * @return True if the token exists, false otherwise.
   */
  function exists(uint256 tokenId_) external view returns (bool);

  /**
   * @dev Checks if the specified address is exempt from ERC-721 transfers.
   * This function retrieves the exemption status for ERC-721 transfers for the given address.
   * 
   * @param target_ The address to check for ERC-721 transfer exemption.
   * @return isExempt True if the address is exempt from ERC-721 transfers, false otherwise.
   */
  function isERC721TransferExempt(address target_) external view returns (bool isExempt);

  /**
   * @dev Sets the exemption status for ERC-721 transfers for the caller.
   * 
   * Emits:
   * - {Transfer} event for each target_ ERC721 token from/to the stash.
   * 
   * @param state_ The new exemption state to set (true for exempt, false for non-exempt).
   */
  function setERC721TransferExempt(bool state_) external;

  /**
   * @dev Retrieves the IDs of ERC-721 tokens owned by a specific address.
   * 
   * @param owner_ The address for which ERC-721 token IDs are being retrieved.
   * @return ownedCreatureIds An array of uint256 representing the ERC-721 token IDs owned by the specified address.
   */
  function owned(address owner_) external view returns (uint256[] memory);

  /**
   * @dev External function to get the current stash address.
   * 
   * @return address Current stash address.
   */
  function stashAddress() external view returns (address);

  /**
   * @dev External function to get the current length of the stash queue.
   * 
   * @return uint256 Current length of the stash queue.
   */
  function stashLength() external view returns (uint256);

  /**
   * @dev External function to retrieve all tokens currently in the stash queue.
   * 
   * @return uint256[] An array containing all tokens currently in the stash queue.
   */
  function tokensInStash() external view returns (uint256[] memory);

  /**
   * @dev Public function to exchange an ERC-721 token with a token in the stash.
   * 
   * Requirements:
   * - The caller must be the owner or have approval to transfer the tokenId_.
   * - The stashTokenId_ must belong to the stash.
   * 
   * Emits:
   * - {Transfer} event for the token exchanged from the stash to the caller.
   * - {Transfer} event for the token exchanged from the caller to the stash.
   * 
   * @param tokenId_ The ID of the ERC-721 token to exchange.
   * @param index_ The index of the token at the stash to exchange with.
   */
  function exchangeWithStash(uint256 tokenId_, uint128 index_) external;
}

File 28 of 35 : IERC404BErrors.sol
// contracts/erc404/IERC404BErrors.sol
// SPDX-License-Identifier: MIT

/**

    /3333333     /33333333     /333333    
   | 33__  33   | 33_____/    /33__  33   
   | 33  \ 33   | 33         | 33  \__/   
   | 3333333    | 33333      | 33         
   | 33__  33   | 33__/      | 33         
   | 33  \ 33   | 33         | 33    33   
   | 3333333/   | 33333333   |  333333/   
   |_______/    |________/    \______/    

 # https://blackeyedcreatures.com

 */
pragma solidity ^0.8.25;

/**
 * @dev ERC404B3Errors interface defines custom error messages for the ERC404B3 contract.
 */
interface IERC404BErrors {

  /**
   * @dev Indicates that the specified token is not found in the stash.
   * @param stashTokenId The ID of the token in the stash.
   */
  error ERC404TokenNotInStash(uint256 stashTokenId);

  /**
   * @dev Indicates that the index value provided is not valid in the stash queue.
   * @param indexInQueue The index value in the queue.
   * @param stashTokenId The ID of the token in the stash.
   */
  error ERC404NotValidIndexValueInStash(uint128 indexInQueue, uint256 stashTokenId);

  /**
   * @dev Indicates that the transfer value is invalid.
   * @param value The invalid transfer value.
   */
  error ERC404InvalidTransferValue(uint256 value);

  /**
   * @dev Indicates that the target address is not eligible for ERC721 exemption.
   * @param target The address that is not eligible for exemption.
   */
  error ERC404InvalidERC721Exemption(address target);

  /**
   * @dev Indicates an overflow in the owned index.
   * @param index The index value causing the overflow.
   */
  error ERC404OwnedIndexOverflow(uint256 index);

  /**
   * @dev Indicates an invalid mint quantity for ERC721.
   */
  error ERC721InvalidMintQuantity();

  /**
   * @dev Indicates that the recipient address has not implemented ERC721Receiver.
   * @param to The recipient address.
   * @param tokenId The ID of the token being transferred.
   * @param quantity The quantity of tokens being transferred.
   * @param data Additional data for the transfer.
   */
  error ERC721ReceiverNotImplemented(address to, uint256 tokenId, uint256 quantity, bytes data);

  /**
   * @dev Indicates that the permit deadline has expired for EIP-2612 permit.
   * @param owner The owner of the tokens.
   * @param spender The spender of the tokens.
   * @param value The token value.
   * @param deadline The expired deadline.
   * @param v Signature parameter v.
   * @param r Signature parameter r.
   * @param s Signature parameter s.
   */
  error EIP2612PermitDeadlineExpired(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s);

  /**
   * @dev Indicates that the signer of the permit is invalid for EIP-2612 permit.
   * @param recoveredAddress The recovered signer address.
   * @param owner The owner of the tokens.
   * @param spender The spender of the tokens.
   * @param value The token value.
   * @param deadline The permit deadline.
   * @param v Signature parameter v.
   * @param r Signature parameter r.
   * @param s Signature parameter s.
   */
  error EIP2612InvalidSigner(address recoveredAddress, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s);
}

File 29 of 35 : DoubleEndedQueue.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/DoubleEndedQueue.sol)
// Modified by Pandora Labs to support native uint256 operations
// Modified by 42i.co to retriveQueueItems && setIndexValue
pragma solidity ^0.8.25;

/**
 * @dev A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends of
 * the sequence (called front and back). Among other access patterns, it can be used to implement efficient LIFO and
 * FIFO queues. Storage use is optimized, and all operations are O(1) constant time. This includes {clear}, given that
 * the existing queue contents are left in storage.
 *
 * The struct is called `Uint256Deque`. This data structure can only be used in storage, and not in memory.
 *
 * ```solidity
 * DoubleEndedQueue.Uint256Deque queue;
 * ```
 */
library DoubleEndedQueue {
  /**
   * @dev An operation (e.g. {front}) couldn't be completed due to the queue being empty.
   */
  error QueueEmpty();

  /**
   * @dev A push operation couldn't be completed due to the queue being full.
   */
  error QueueFull();

  /**
   * @dev An operation (e.g. {at}) couldn't be completed due to an index being out of bounds.
   */
  error QueueOutOfBounds();

  /**
   * @dev Indices are 128 bits so begin and end are packed in a single storage slot for efficient access.
   *
   * Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
   * directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
   * lead to unexpected behavior.
   *
   * The first item is at data[begin] and the last item is at data[end - 1]. This range can wrap around.
   */
  struct Uint256Deque {
    uint128 _begin;
    uint128 _end;
    mapping(uint128 index => uint256) _data;
  }

  /**
   * @dev Retrieves all items from the queue as an array of uint256 values.
   * This function does not modify the state of the queue.
   */
  function retriveQueueItems(Uint256Deque storage deque) internal view returns (uint256[] memory dequeTokens) {
    unchecked {
      uint128 beginIndex = deque._begin;
      uint128 queueLenght = deque._end - beginIndex;
      dequeTokens = new uint256[](queueLenght);
      for (uint128 i = 0; i < queueLenght; ++i) {
        dequeTokens[i] = deque._data[beginIndex+i];
      }
    }
  }

  /**
   * @dev Sets the value at a specific index in the queue. The index is relative to the current `begin` of the queue.
   * This function allows for direct manipulation of queue items at a given index, and should be used with caution as
   * improper usage can lead to logical errors in the queue state.
   *
   * Reverts with `QueueOutOfBounds` if the index is out of bounds.
   */
  function setIndexValue(Uint256Deque storage deque, uint128 index, uint256 value) internal {
    if (index >= length(deque)) revert QueueOutOfBounds();
    deque._data[index+deque._begin] = value;
  }

  /**
   * @dev Inserts an item at the end of the queue.
   *
   * Reverts with {QueueFull} if the queue is full.
   */
  function pushBack(Uint256Deque storage deque, uint256 value) internal {
    unchecked {
      uint128 backIndex = deque._end;
      if (backIndex + 1 == deque._begin) revert QueueFull();
      deque._data[backIndex] = value;
      deque._end = backIndex + 1;
    }
  }

  /**
   * @dev Removes the item at the end of the queue and returns it.
   *
   * Reverts with {QueueEmpty} if the queue is empty.
   */
  function popBack(
    Uint256Deque storage deque
  ) internal returns (uint256 value) {
    unchecked {
      uint128 backIndex = deque._end;
      if (backIndex == deque._begin) revert QueueEmpty();
      --backIndex;
      value = deque._data[backIndex];
      delete deque._data[backIndex];
      deque._end = backIndex;
    }
  }

  /**
   * @dev Inserts an item at the beginning of the queue.
   *
   * Reverts with {QueueFull} if the queue is full.
   */
  function pushFront(Uint256Deque storage deque, uint256 value) internal {
    unchecked {
      uint128 frontIndex = deque._begin - 1;
      if (frontIndex == deque._end) revert QueueFull();
      deque._data[frontIndex] = value;
      deque._begin = frontIndex;
    }
  }

  /**
   * @dev Removes the item at the beginning of the queue and returns it.
   *
   * Reverts with `QueueEmpty` if the queue is empty.
   */
  function popFront(
    Uint256Deque storage deque
  ) internal returns (uint256 value) {
    unchecked {
      uint128 frontIndex = deque._begin;
      if (frontIndex == deque._end) revert QueueEmpty();
      value = deque._data[frontIndex];
      delete deque._data[frontIndex];
      deque._begin = frontIndex + 1;
    }
  }

  /**
   * @dev Returns the item at the beginning of the queue.
   *
   * Reverts with `QueueEmpty` if the queue is empty.
   */
  function front(
    Uint256Deque storage deque
  ) internal view returns (uint256 value) {
    if (empty(deque)) revert QueueEmpty();
    return deque._data[deque._begin];
  }

  /**
   * @dev Returns the item at the end of the queue.
   *
   * Reverts with `QueueEmpty` if the queue is empty.
   */
  function back(
    Uint256Deque storage deque
  ) internal view returns (uint256 value) {
    if (empty(deque)) revert QueueEmpty();
    unchecked {
      return deque._data[deque._end - 1];
    }
  }

  /**
   * @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at
   * `length(deque) - 1`.
   *
   * Reverts with `QueueOutOfBounds` if the index is out of bounds.
   */
  function at(
    Uint256Deque storage deque,
    uint256 index
  ) internal view returns (uint256 value) {
    if (index >= length(deque)) revert QueueOutOfBounds();
    // By construction, length is a uint128, so the check above ensures that index can be safely downcast to uint128
    unchecked {
      return deque._data[deque._begin + uint128(index)];
    }
  }

  /**
   * @dev Resets the queue back to being empty.
   *
   * NOTE: The current items are left behind in storage. This does not affect the functioning of the queue, but misses
   * out on potential gas refunds.
   */
  function clear(Uint256Deque storage deque) internal {
    deque._begin = 0;
    deque._end = 0;
  }

  /**
   * @dev Returns the number of items in the queue.
   */
  function length(Uint256Deque storage deque) internal view returns (uint256) {
    unchecked {
      return uint256(deque._end - deque._begin);
    }
  }

  /**
   * @dev Returns true if the queue is empty.
   */
  function empty(Uint256Deque storage deque) internal view returns (bool) {
    return deque._end == deque._begin;
  }
}

File 30 of 35 : IBECErrors.sol
// contracts/IBECErrors.sol
// SPDX-License-Identifier: MIT

/**

    /3333333     /33333333     /333333    
   | 33__  33   | 33_____/    /33__  33   
   | 33  \ 33   | 33         | 33  \__/   
   | 3333333    | 33333      | 33         
   | 33__  33   | 33__/      | 33         
   | 33  \ 33   | 33         | 33    33   
   | 3333333/   | 33333333   |  333333/   
   |_______/    |________/    \______/    

 # https://blackeyedcreatures.com

 */
pragma solidity ^0.8.25;

/**
 * @title IBECErrors
 * @dev Interface for Black Eyed Creatures (BEC) custom errors.
 * @author https://42i.co
 */
interface IBECErrors {

    /**
     * @dev Indicates that a minting operation attempted to mint zero creatures.
     */
    error BECZeroQuantityMinting();

    /**
     * @dev Indicates that a minting operation has exceeded the cap limit for creatures.
     */
    error BECCapLimitReached();

    /**
     * @dev Indicates that invalid ambassadors were provided.
     * @param alpha ID of the first ambassador.
     * @param beta ID of the second ambassador.
     * @param gamma ID of the third ambassador.
     */
    error BECInvalidAmbassadors(uint256 alpha, uint256 beta, uint256 gamma);

    /**
     * @dev Indicates that an incorrect number of ambassadors was provided.
     * @param length The number of ambassadors provided.
     */
    error BECInvalidAmbassadorsLength(uint256 length);

    /**
     * @dev Indicates that a creature is already set as a variation ambassador.
     * @param creatureId The ID of the creature.
     */
    error BECAlreadyVariationAmbassador(uint256 creatureId);

    /**
     * @dev Indicates that a creature is already set as a family ambassador.
     * @param creatureId The ID of the creature.
     */
    error BECAlreadyFamilyAmbassador(uint256 creatureId);

    /**
     * @dev Indicates that a creature is not a variation ambassador when required.
     * @param creatureId The ID of the creature.
     */
    error BECNotVariationAmbassador(uint256 creatureId);

    /**
     * @dev Indicates that an invalid phenotype was provided.
     * @param phenotype The invalid phenotype identifier.
     */
    error BECInvalidPhenotype(uint256 phenotype);

    /**
     * @dev Indicates that an invalid family was provided for a creature.
     * @param creatureId The ID of the creature.
     */
    error BECInvalidFamily(uint256 creatureId);

    /**
     * @dev Indicates that an invalid variation was provided for a creature.
     * @param creatureId The ID of the creature.
     */
    error BECInvalidVariation(uint256 creatureId);

    /**
     * @dev Indicates that an invalid name was provided, either empty or too long.
     * @param name The invalid name submitted.
     */
    error BECInvalidName(string name);

    /**
     * @dev Indicates that a duplicated name was provided.
     * @param name The duplicate name submitted.
     */
    error BECDuplicatedName(string name);

    /**
     * @dev Indicates that a variation name has already been set for a given phenotype, family, and variation.
     * @param phenotype The phenotype identifier.
     * @param family The family identifier.
     * @param variation The variation identifier.
     */
    error BECVariationNameAlreadySet(uint256 phenotype, uint256 family, uint256 variation);

    /**
     * @dev Indicates that a family name has already been set for a given phenotype and family.
     * @param phenotype The phenotype identifier.
     * @param family The family identifier.
     */
    error BECFamilyNameAlreadySet(uint256 phenotype, uint256 family);

    /**
     * @dev Indicates that not all variations have been set for a given phenotype and family.
     * @param phenotype The phenotype identifier.
     * @param family The family identifier.
     * @param variation The variation identifier that is missing.
     */
    error BECNotAllVariationsSet(uint256 phenotype, uint256 family, uint256 variation);

    /**
     * @dev Indicates that not all required variations were present.
     */
    error BECNotAllVariationsPresent();

    /**
     * @dev Indicates that a hunt has already finished.
     */
    error BECHuntFinished();

    /**
     * @dev Indicates that the ritual price was not fully paid.
     * @param price The expected price.
     * @param paid The amount that was actually paid.
     */
    error BECRitualPriceNotPaid(uint256 price, uint256 paid);

    /**
     * @dev Indicates that the maximum number of hunts allowed was exceeded.
     * @param allowed The allowed number of hunts.
     * @param required The required number of hunts attempted.
     */
    error BECMaxHuntsAllowed(uint256 allowed, uint256 required);

    /**
     * @dev Indicates that the maximum number of reproductions allowed was exceeded.
     * @param allowed The allowed number of reproductions.
     * @param required The required number of reproductions attempted.
     */
    error BECMaxReproductionsAllowed(uint256 allowed, uint256 required);

    /**
     * @dev Indicates that the maximum number of reproductions for a specific creature was exceeded.
     * @param creatureId The ID of the creature.
     * @param allowed The allowed number of reproductions for this creature.
     * @param required The required number of reproductions attempted.
     */
    error BECMaxCreatureReproductionsAllowed(uint256 creatureId, uint256 allowed, uint256 required);

    /**
     * @dev Indicates that the creature is not of type 0, which is required for the operation.
     * @param creatureId The ID of the creature.
     */
    error BECNotType0Creature(uint256 creatureId);

    /**
     * @dev Indicates that the creature is fruitless, meaning it cannot reproduce.
     * @param creatureId The ID of the creature.
     */
    error BECFruitlessCreature(uint256 creatureId);

    /**
     * @dev Indicates that a parent creature was attempted to be used as payment, which is not allowed.
     * @param creatureId The ID of the creature.
     */
    error BECParentCreatureAsPayment(uint256 creatureId);

    /**
     * @dev Indicates that an invalid owner was provided for a token.
     * @param operator The address of the operator trying to act on the token.
     * @param tokenId The token ID being acted upon.
     */
    error BECInvalidOwner(address operator, uint256 tokenId);

    /**
     * @dev Indicates that there are insufficient funds for the requested operation.
     * @param requested The amount of funds requested.
     * @param available The amount of funds available.
     */
    error BECInsufficientFunds(uint256 requested, uint256 available);

    /**
     * @dev Indicates that an invalid owner was provided for a Black Sphere.
     * @param owner The incorrect owner address.
     * @param blacksphere The Black Sphere ID involved.
     */
    error BlackSphereInvalidOwner(address owner, uint256 blacksphere);

    /**
     * @dev Indicates that the function or feature called is not currently enabled.
     */
    error BECNotEnabled();

    /**
     * @dev Indicates that the genesis store for a creature has already been set.
     * @param creatureId The ID of the creature.
     */
    error BECGenesisStoreAlreadySet(uint256 creatureId);

    /**
     * @dev Indicates that the distribution for a creature has already been set.
     * @param distribution The distribution ID.
     */
    error BECDistributionAlreadySet(uint256 distribution);

    /**
     * @dev Indicates that the wrong distribution ID was provided.
     * @param distributionId The incorrect distribution ID provided.
     */
    error BECWrongDistributionId(uint256 distributionId);

    /**
     * @dev Indicates that the domain for a creature has already been set.
     * @param domain The domain ID.
     */
    error BECDomainAlreadySet(uint256 domain);

    /**
     * @dev Indicates that an empty array was provided for golden data.
     */
    error BECEmptyGoldenDataArray();

    /**
     * @dev Indicates that a non-empty golden deltas array was provided when expected to be empty.
     */
    error BECNotEmptyGoldenDeltasArray();

    /**
     * @dev Indicates that more creatures were provided than there are phenotypes available.
     */
    error BECMoreCreaturesThanPhenotypes();

    /**
     * @dev Indicates that an awakening operation has already been performed.
     */
    error BECAlreadyAwaken();

    /**
     * @dev Indicates that IDs were not provided in order.
     */
    error BECIdsNotInOrder();

    /**
     * @dev Indicates that an invalid address was provided.
     * @param target The target address.
     */
    error BECInvalidAddress(address target);

    /**
     * @dev Indicates that an invalid protocol bit was used.
     */
    error BECInvalidProtocolBit();

    /**
     * @dev Indicates that an index was out of bounds.
     */
    error BECIndexOutOfBounds();
}

File 31 of 35 : IBECMetaExtension.sol
// contracts/meta-extensions/IBECMetaExtension.sol
// SPDX-License-Identifier: MIT

/**

    /3333333     /33333333     /333333    
   | 33__  33   | 33_____/    /33__  33   
   | 33  \ 33   | 33         | 33  \__/   
   | 3333333    | 33333      | 33         
   | 33__  33   | 33__/      | 33         
   | 33  \ 33   | 33         | 33    33   
   | 3333333/   | 33333333   |  333333/   
   |_______/    |________/    \______/    

 # https://blackeyedcreatures.com

 */

pragma solidity ^0.8.25;

/**
 * @title Black Eyed Creatures Meta Extension Interface
 * @dev Defines the interface for meta extensions within the Black Eyed Creatures ecosystem. 
 *      Meta extensions are modular contracts that provide additional token metadata for creatures, enhancing their attributes, 
 *      appearance, or interactions within the ecosystem.
 * @notice Implementations of this interface should ensure that extensions are applied in a manner that is consistent with 
 *         the ecosystem's rules and enhances the user experience.
 * @author https://42i.co
 */
interface IBECMetaExtension {

  /**
   * @notice Applies the meta extension to a creature and returns the additional metadata.
   * @dev When implemented, this function should handle the logic for appending the creature's metadata based on the extension's purpose. 
   * @param _creatureId The unique identifier of the creature to which the extension is applied.
   * @return string A string representing the modified or additional metadata for the creature.
   */
  function applyExtension(uint256 _creatureId) external view returns (string memory);
}

File 32 of 35 : IBECDnaStrategy.sol
// contracts/strategies/IBECDnaStrategy.sol
// SPDX-License-Identifier: MIT

/**

    /3333333     /33333333     /333333    
   | 33__  33   | 33_____/    /33__  33   
   | 33  \ 33   | 33         | 33  \__/   
   | 3333333    | 33333      | 33         
   | 33__  33   | 33__/      | 33         
   | 33  \ 33   | 33         | 33    33   
   | 3333333/   | 33333333   |  333333/   
   |_______/    |________/    \______/    

 # https://blackeyedcreatures.com

 */

pragma solidity ^0.8.25;

/**
 * @title Black Eyed Creatures DNA Strategy
 * @notice The strategies are the contracts that mints and generates the metada for the BECs. 
 *         This interface helps with the information the genesis engine needs for each creature type.
 * @dev The genesis engine (through different strategies) creates DNA based in different logis, 
 *      but always creating DNA that are compliant with the following order (8b each):
 * 00: body_family
 * 01: body_variation
 * 02: head_family
 * 03: head_variation
 * 04: ears_family
 * 05: ears_variation
 * 06: face_family
 * 07: face_variation
 * 08: hair_family
 * 09: hair_variation
 * 10: eyes_family
 * 11: eyes_variation
 * 12: bg_color_family
 * 13: bg_color_variation
 * 14: skin_color_family
 * 15: skin_color_variation
 * 16: secondary_color_family
 * 17: secondary_color_variation
 * 18-30: unused
 * 31: type: normal|golden|ghost|others
 *
 * @author https://42i.co
 */
interface IBECDnaStrategy {
  /**
   * @notice Retrieves the DNA of a creature based on its id.
   * @param _creatureId The id of the creature.
   * @return uint256 The DNA of the specified creature.
   */
  function getDna(uint256 _creatureId) external view returns (uint256);

  /**
   * @notice Gets the metadata tokenURI for this creature.
   * @param _creatureId The id of the creature.
   * @return string memory The token uri.
   */
  function tokenURI(uint256 _creatureId) external view returns (string memory);

  /**
   * @notice Identifies the strategy ID.
   * @return uint256 The ID of the strategy.
   */
  function getStrategyId() external view returns (uint256);

  /**
   * @notice Identifies the distribution ID associated with this strategy.
   * @return uint256 The distribution ID.
   */
  function getDistributionId() external view returns (uint256);
}

File 33 of 35 : BitMaps.sol
// SPDX-License-Identifier: MIT
/**
   _____       ___     ___ __           ____  _ __      
  / ___/____  / (_)___/ (_) /___  __   / __ )(_) /______
  \__ \/ __ \/ / / __  / / __/ / / /  / __  / / __/ ___/
 ___/ / /_/ / / / /_/ / / /_/ /_/ /  / /_/ / / /_(__  ) 
/____/\____/_/_/\__,_/_/\__/\__, /  /_____/_/\__/____/  
                           /____/                        

- npm: https://www.npmjs.com/package/solidity-bits
- github: https://github.com/estarriolvetch/solidity-bits

 */
pragma solidity ^0.8.0;

import "./BitScan.sol";
import "./Popcount.sol";

/**
 * @dev This Library is a modified version of Openzeppelin's BitMaps library with extra features.
 *
 * 1. Functions of finding the index of the closest set bit from a given index are added.
 *    The indexing of each bucket is modifed to count from the MSB to the LSB instead of from the LSB to the MSB.
 *    The modification of indexing makes finding the closest previous set bit more efficient in gas usage.
 * 2. Setting and unsetting the bitmap consecutively.
 * 3. Accounting number of set bits within a given range.   
 *
*/

/**
 * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
 * Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
 */

library BitMaps {
    using BitScan for uint256;
    uint256 private constant MASK_INDEX_ZERO = (1 << 255);
    uint256 private constant MASK_FULL = type(uint256).max;

    struct BitMap {
        mapping(uint256 => uint256) _data;
    }

    /**
     * @dev Returns whether the bit at `index` is set.
     */
    function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
        uint256 bucket = index >> 8;
        uint256 mask = MASK_INDEX_ZERO >> (index & 0xff);
        return bitmap._data[bucket] & mask != 0;
    }

    /**
     * @dev Sets the bit at `index` to the boolean `value`.
     */
    function setTo(
        BitMap storage bitmap,
        uint256 index,
        bool value
    ) internal {
        if (value) {
            set(bitmap, index);
        } else {
            unset(bitmap, index);
        }
    }

    /**
     * @dev Sets the bit at `index`.
     */
    function set(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = MASK_INDEX_ZERO >> (index & 0xff);
        bitmap._data[bucket] |= mask;
    }

    /**
     * @dev Unsets the bit at `index`.
     */
    function unset(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = MASK_INDEX_ZERO >> (index & 0xff);
        bitmap._data[bucket] &= ~mask;
    }


    /**
     * @dev Consecutively sets `amount` of bits starting from the bit at `startIndex`.
     */    
    function setBatch(BitMap storage bitmap, uint256 startIndex, uint256 amount) internal {
        uint256 bucket = startIndex >> 8;

        uint256 bucketStartIndex = (startIndex & 0xff);

        unchecked {
            if(bucketStartIndex + amount < 256) {
                bitmap._data[bucket] |= MASK_FULL << (256 - amount) >> bucketStartIndex;
            } else {
                bitmap._data[bucket] |= MASK_FULL >> bucketStartIndex;
                amount -= (256 - bucketStartIndex);
                bucket++;

                while(amount > 256) {
                    bitmap._data[bucket] = MASK_FULL;
                    amount -= 256;
                    bucket++;
                }

                bitmap._data[bucket] |= MASK_FULL << (256 - amount);
            }
        }
    }


    /**
     * @dev Consecutively unsets `amount` of bits starting from the bit at `startIndex`.
     */    
    function unsetBatch(BitMap storage bitmap, uint256 startIndex, uint256 amount) internal {
        uint256 bucket = startIndex >> 8;

        uint256 bucketStartIndex = (startIndex & 0xff);

        unchecked {
            if(bucketStartIndex + amount < 256) {
                bitmap._data[bucket] &= ~(MASK_FULL << (256 - amount) >> bucketStartIndex);
            } else {
                bitmap._data[bucket] &= ~(MASK_FULL >> bucketStartIndex);
                amount -= (256 - bucketStartIndex);
                bucket++;

                while(amount > 256) {
                    bitmap._data[bucket] = 0;
                    amount -= 256;
                    bucket++;
                }

                bitmap._data[bucket] &= ~(MASK_FULL << (256 - amount));
            }
        }
    }

    /**
     * @dev Returns number of set bits within a range.
     */
    function popcountA(BitMap storage bitmap, uint256 startIndex, uint256 amount) internal view returns(uint256 count) {
        uint256 bucket = startIndex >> 8;

        uint256 bucketStartIndex = (startIndex & 0xff);

        unchecked {
            if(bucketStartIndex + amount < 256) {
                count +=  Popcount.popcount256A(
                    bitmap._data[bucket] & (MASK_FULL << (256 - amount) >> bucketStartIndex)
                );
            } else {
                count += Popcount.popcount256A(
                    bitmap._data[bucket] & (MASK_FULL >> bucketStartIndex)
                );
                amount -= (256 - bucketStartIndex);
                bucket++;

                while(amount > 256) {
                    count += Popcount.popcount256A(bitmap._data[bucket]);
                    amount -= 256;
                    bucket++;
                }
                count += Popcount.popcount256A(
                    bitmap._data[bucket] & (MASK_FULL << (256 - amount))
                );
            }
        }
    }

    /**
     * @dev Returns number of set bits within a range.
     */
    function popcountB(BitMap storage bitmap, uint256 startIndex, uint256 amount) internal view returns(uint256 count) {
        uint256 bucket = startIndex >> 8;

        uint256 bucketStartIndex = (startIndex & 0xff);

        unchecked {
            if(bucketStartIndex + amount < 256) {
                count +=  Popcount.popcount256B(
                    bitmap._data[bucket] & (MASK_FULL << (256 - amount) >> bucketStartIndex)
                );
            } else {
                count += Popcount.popcount256B(
                    bitmap._data[bucket] & (MASK_FULL >> bucketStartIndex)
                );
                amount -= (256 - bucketStartIndex);
                bucket++;

                while(amount > 256) {
                    count += Popcount.popcount256B(bitmap._data[bucket]);
                    amount -= 256;
                    bucket++;
                }
                count += Popcount.popcount256B(
                    bitmap._data[bucket] & (MASK_FULL << (256 - amount))
                );
            }
        }
    }


    /**
     * @dev Find the closest index of the set bit before `index`.
     */
    function scanForward(BitMap storage bitmap, uint256 index) internal view returns (uint256 setBitIndex) {
        uint256 bucket = index >> 8;

        // index within the bucket
        uint256 bucketIndex = (index & 0xff);

        // load a bitboard from the bitmap.
        uint256 bb = bitmap._data[bucket];

        // offset the bitboard to scan from `bucketIndex`.
        bb = bb >> (0xff ^ bucketIndex); // bb >> (255 - bucketIndex)
        
        if(bb > 0) {
            unchecked {
                setBitIndex = (bucket << 8) | (bucketIndex -  bb.bitScanForward256());    
            }
        } else {
            while(true) {
                require(bucket > 0, "BitMaps: The set bit before the index doesn't exist.");
                unchecked {
                    bucket--;
                }
                // No offset. Always scan from the least significiant bit now.
                bb = bitmap._data[bucket];
                
                if(bb > 0) {
                    unchecked {
                        setBitIndex = (bucket << 8) | (255 -  bb.bitScanForward256());
                        break;
                    }
                } 
            }
        }
    }

    function getBucket(BitMap storage bitmap, uint256 bucket) internal view returns (uint256) {
        return bitmap._data[bucket];
    }
}

File 34 of 35 : BitScan.sol
// SPDX-License-Identifier: MIT
/**
   _____       ___     ___ __           ____  _ __      
  / ___/____  / (_)___/ (_) /___  __   / __ )(_) /______
  \__ \/ __ \/ / / __  / / __/ / / /  / __  / / __/ ___/
 ___/ / /_/ / / / /_/ / / /_/ /_/ /  / /_/ / / /_(__  ) 
/____/\____/_/_/\__,_/_/\__/\__, /  /_____/_/\__/____/  
                           /____/                        

- npm: https://www.npmjs.com/package/solidity-bits
- github: https://github.com/estarriolvetch/solidity-bits

 */

pragma solidity ^0.8.0;


library BitScan {
    uint256 constant private DEBRUIJN_256 = 0x818283848586878898a8b8c8d8e8f929395969799a9b9d9e9faaeb6bedeeff;
    bytes constant private LOOKUP_TABLE_256 = hex"0001020903110a19042112290b311a3905412245134d2a550c5d32651b6d3a7506264262237d468514804e8d2b95569d0d495ea533a966b11c886eb93bc176c9071727374353637324837e9b47af86c7155181ad4fd18ed32c9096db57d59ee30e2e4a6a5f92a6be3498aae067ddb2eb1d5989b56fd7baf33ca0c2ee77e5caf7ff0810182028303840444c545c646c7425617c847f8c949c48a4a8b087b8c0c816365272829aaec650acd0d28fdad4e22d6991bd97dfdcea58b4d6f29fede4f6fe0f1f2f3f4b5b6b607b8b93a3a7b7bf357199c5abcfd9e168bcdee9b3f1ecf5fd1e3e5a7a8aa2b670c4ced8bbe8f0f4fc3d79a1c3cde7effb78cce6facbf9f8";

    /**
        @dev Isolate the least significant set bit.
     */ 
    function isolateLS1B256(uint256 bb) pure internal returns (uint256) {
        require(bb > 0);
        unchecked {
            return bb & (0 - bb);
        }
    } 

    /**
        @dev Isolate the most significant set bit.
     */ 
    function isolateMS1B256(uint256 bb) pure internal returns (uint256) {
        require(bb > 0);
        unchecked {
            bb |= bb >> 128;
            bb |= bb >> 64;
            bb |= bb >> 32;
            bb |= bb >> 16;
            bb |= bb >> 8;
            bb |= bb >> 4;
            bb |= bb >> 2;
            bb |= bb >> 1;
            
            return (bb >> 1) + 1;
        }
    } 

    /**
        @dev Find the index of the lest significant set bit. (trailing zero count)
     */ 
    function bitScanForward256(uint256 bb) pure internal returns (uint8) {
        unchecked {
            return uint8(LOOKUP_TABLE_256[(isolateLS1B256(bb) * DEBRUIJN_256) >> 248]);
        }   
    }

    /**
        @dev Find the index of the most significant set bit.
     */ 
    function bitScanReverse256(uint256 bb) pure internal returns (uint8) {
        unchecked {
            return 255 - uint8(LOOKUP_TABLE_256[((isolateMS1B256(bb) * DEBRUIJN_256) >> 248)]);
        }   
    }

    function log2(uint256 bb) pure internal returns (uint8) {
        unchecked {
            return uint8(LOOKUP_TABLE_256[(isolateMS1B256(bb) * DEBRUIJN_256) >> 248]);
        } 
    }
}

File 35 of 35 : Popcount.sol
// SPDX-License-Identifier: MIT
/**
   _____       ___     ___ __           ____  _ __      
  / ___/____  / (_)___/ (_) /___  __   / __ )(_) /______
  \__ \/ __ \/ / / __  / / __/ / / /  / __  / / __/ ___/
 ___/ / /_/ / / / /_/ / / /_/ /_/ /  / /_/ / / /_(__  ) 
/____/\____/_/_/\__,_/_/\__/\__, /  /_____/_/\__/____/  
                           /____/                        

- npm: https://www.npmjs.com/package/solidity-bits
- github: https://github.com/estarriolvetch/solidity-bits

 */

pragma solidity ^0.8.0;

library Popcount {
    uint256 private constant m1 = 0x5555555555555555555555555555555555555555555555555555555555555555;
    uint256 private constant m2 = 0x3333333333333333333333333333333333333333333333333333333333333333;
    uint256 private constant m4 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
    uint256 private constant h01 = 0x0101010101010101010101010101010101010101010101010101010101010101;

    function popcount256A(uint256 x) internal pure returns (uint256 count) {
        unchecked{
            for (count=0; x!=0; count++)
                x &= x - 1;
        }
    }

    function popcount256B(uint256 x) internal pure returns (uint256) {
        if (x == type(uint256).max) {
            return 256;
        }
        unchecked {
            x -= (x >> 1) & m1;             //put count of each 2 bits into those 2 bits
            x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits 
            x = (x + (x >> 4)) & m4;        //put count of each 8 bits into those 8 bits 
            x = (x * h01) >> 248;  //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... 
        }
        return x;
    }
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 200,
    "details": {
      "yulDetails": {
        "optimizerSteps": "u"
      }
    }
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"BECAlreadyAwaken","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatureId","type":"uint256"}],"name":"BECAlreadyFamilyAmbassador","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatureId","type":"uint256"}],"name":"BECAlreadyVariationAmbassador","type":"error"},{"inputs":[],"name":"BECCapLimitReached","type":"error"},{"inputs":[{"internalType":"uint256","name":"distribution","type":"uint256"}],"name":"BECDistributionAlreadySet","type":"error"},{"inputs":[{"internalType":"uint256","name":"domain","type":"uint256"}],"name":"BECDomainAlreadySet","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"BECDuplicatedName","type":"error"},{"inputs":[],"name":"BECEmptyGoldenDataArray","type":"error"},{"inputs":[{"internalType":"uint256","name":"phenotype","type":"uint256"},{"internalType":"uint256","name":"family","type":"uint256"}],"name":"BECFamilyNameAlreadySet","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatureId","type":"uint256"}],"name":"BECFruitlessCreature","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatureId","type":"uint256"}],"name":"BECGenesisStoreAlreadySet","type":"error"},{"inputs":[],"name":"BECHuntFinished","type":"error"},{"inputs":[],"name":"BECIdsNotInOrder","type":"error"},{"inputs":[],"name":"BECIndexOutOfBounds","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"BECInsufficientFunds","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"BECInvalidAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"alpha","type":"uint256"},{"internalType":"uint256","name":"beta","type":"uint256"},{"internalType":"uint256","name":"gamma","type":"uint256"}],"name":"BECInvalidAmbassadors","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"BECInvalidAmbassadorsLength","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatureId","type":"uint256"}],"name":"BECInvalidFamily","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"BECInvalidName","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"BECInvalidOwner","type":"error"},{"inputs":[{"internalType":"uint256","name":"phenotype","type":"uint256"}],"name":"BECInvalidPhenotype","type":"error"},{"inputs":[],"name":"BECInvalidProtocolBit","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatureId","type":"uint256"}],"name":"BECInvalidVariation","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatureId","type":"uint256"},{"internalType":"uint256","name":"allowed","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"BECMaxCreatureReproductionsAllowed","type":"error"},{"inputs":[{"internalType":"uint256","name":"allowed","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"BECMaxHuntsAllowed","type":"error"},{"inputs":[{"internalType":"uint256","name":"allowed","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"BECMaxReproductionsAllowed","type":"error"},{"inputs":[],"name":"BECMoreCreaturesThanPhenotypes","type":"error"},{"inputs":[],"name":"BECNotAllVariationsPresent","type":"error"},{"inputs":[{"internalType":"uint256","name":"phenotype","type":"uint256"},{"internalType":"uint256","name":"family","type":"uint256"},{"internalType":"uint256","name":"variation","type":"uint256"}],"name":"BECNotAllVariationsSet","type":"error"},{"inputs":[],"name":"BECNotEmptyGoldenDeltasArray","type":"error"},{"inputs":[],"name":"BECNotEnabled","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatureId","type":"uint256"}],"name":"BECNotType0Creature","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatureId","type":"uint256"}],"name":"BECNotVariationAmbassador","type":"error"},{"inputs":[{"internalType":"uint256","name":"creatureId","type":"uint256"}],"name":"BECParentCreatureAsPayment","type":"error"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"paid","type":"uint256"}],"name":"BECRitualPriceNotPaid","type":"error"},{"inputs":[{"internalType":"uint256","name":"phenotype","type":"uint256"},{"internalType":"uint256","name":"family","type":"uint256"},{"internalType":"uint256","name":"variation","type":"uint256"}],"name":"BECVariationNameAlreadySet","type":"error"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"}],"name":"BECWrongDistributionId","type":"error"},{"inputs":[],"name":"BECZeroQuantityMinting","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"blacksphere","type":"uint256"}],"name":"BlackSphereInvalidOwner","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[{"internalType":"address","name":"recoveredAddress","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"EIP2612InvalidSigner","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"EIP2612PermitDeadlineExpired","type":"error"},{"inputs":[],"name":"EIP712BECSigningKeyNotSet","type":"error"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"EIP712InvalidSignature","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"ERC404InvalidERC721Exemption","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"ERC404InvalidTransferValue","type":"error"},{"inputs":[{"internalType":"uint128","name":"indexInQueue","type":"uint128"},{"internalType":"uint256","name":"stashTokenId","type":"uint256"}],"name":"ERC404NotValidIndexValueInStash","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ERC404OwnedIndexOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"stashTokenId","type":"uint256"}],"name":"ERC404TokenNotInStash","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[],"name":"ERC721InvalidMintQuantity","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"ERC721ReceiverNotImplemented","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"QueueEmpty","type":"error"},{"inputs":[],"name":"QueueFull","type":"error"},{"inputs":[],"name":"QueueOutOfBounds","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"StringsInsufficientHexLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GENETICIST","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"IPFS_URI_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"}],"name":"_erc20Approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"id_","type":"uint256"}],"name":"_erc721Approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"}],"name":"_transferFromERC20","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender_","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"valueOrId_","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_creatureId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"uint128","name":"index_","type":"uint128"}],"name":"exchangeWithStash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_target","type":"address"},{"internalType":"bool","name":"_state","type":"bool"}],"name":"forceERC721TransferExempt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_creatureId","type":"uint256"}],"name":"getImageURI","outputs":[{"internalType":"string","name":"uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_creatureId","type":"uint256"}],"name":"getSeed","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_creatureIds","type":"uint256[]"}],"name":"getSeeds","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSiteBaseURI","outputs":[{"internalType":"string","name":"uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ipfsUris","outputs":[{"internalType":"string","name":"ipfsUri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"operator_","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target_","type":"address"}],"name":"isERC721TransferExempt","outputs":[{"internalType":"bool","name":"isExempt","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_creatureOwner","type":"address"},{"internalType":"bytes32","name":"_seed","type":"bytes32"},{"internalType":"uint256","name":"_quantity","type":"uint256"}],"name":"mintWithSeed","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"owned","outputs":[{"internalType":"uint256[]","name":"ownedCreatureIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender_","type":"address"},{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"deadline_","type":"uint256"},{"internalType":"uint8","name":"v_","type":"uint8"},{"internalType":"bytes32","name":"r_","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signingKey","type":"address"}],"name":"setAllowlistSigningAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator_","type":"address"},{"internalType":"bool","name":"approved_","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_uri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"state_","type":"bool"}],"name":"setERC721TransferExempt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_genesisEngineContractAddress","type":"address"}],"name":"setGenesisEngineContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_creatureId","type":"uint256"},{"internalType":"string","name":"_uri","type":"string"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"setIPFSTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_uri","type":"string"}],"name":"setSiteBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stashAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stashLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_creatureId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokensInStash","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"valueOrId_","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"valueOrId_","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address payable","name":"_address","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address payable","name":"_address","type":"address"}],"name":"withdrawERC20Token","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"address","name":"_address","type":"address"}],"name":"withdrawERC721Token","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]

610140604052346100f5576100126101eb565b604051615bc0610ac78239608051816107e1015260a051818181610f490152818161174e0152818161341c01528181613b2601528181614222015281816142890152818161450e01528181614fa80152615634015260c0518181816112f0015281816120120152818161219b0152818161230e0152818161275b01528181612d3e015281816131a40152818161335d015281816135610152818161447f015281816146a50152818161528a015281816155fa0152615743015260e0518161181f015261010051816118460152610120518181816112480152613c1d0152615bc090f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b90601f01601f191681019081106001600160401b0382111761013157604052565b6100fa565b9061014a61014360405190565b9283610110565b565b6001600160401b03811161013157602090601f01601f19160190565b9061017a6101758361014c565b610136565b918252565b6101896014610168565b7f426c61636b204579656420437265617475726573000000000000000000000000602082015290565b6101ba61017f565b90565b6101c76003610168565b6242454360e81b602082015290565b6101ba6101bd565b6101ba6101ba6101ba9290565b61020f6101f66101b2565b6101fe6101d6565b61020961014d6101de565b91610247565b61014a6000805160206167028339815191527f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a66107ab565b9061014a9291610336565b61025f6101ba6101ba9290565b6001600160a01b031690565b6101ba90610252565b906001600160a01b03905b9181191691161790565b6101ba9061025f906001600160a01b031682565b6101ba90610289565b6101ba9061029d565b906102bf6101ba6102c6926102a6565b8254610274565b9055565b6102d46001610168565b603360f81b602082015290565b6101ba6102ca565b9052565b6102e99061025f565b9095949261014a9461032861032f9261032160809661031a60a088019c6000890152565b6020870152565b6040850152565b6060830152565b01906102ed565b9061034192916103db565b61035561034e600061026b565b60126102af565b61035d6101d6565b61036f610368825190565b9160200190565b206103c961037b6102e1565b610386610368825190565b2091610391306102a6565b926103bd61039e60405190565b948593602085019346916000805160206166e2833981519152866102f6565b90810382520382610110565b6103d4610368825190565b2061012052565b61014a9283929091336103f9565b60208101929161014a91906102ed565b92909161040592610457565b61040f600061026b565b6104188161025f565b6104218361025f565b14610430575061014a90610822565b6104539061043d60405190565b631e4fbdf760e01b8152918291600483016103e9565b0390fd5b906104629291610488565b61048560008051602061670283398151915261047e81806107ab565b33906108a3565b50565b61014a92839290916106d5565b906000199061027f565b906104af6101ba6102c6926101de565b8254610495565b634e487b7160e01b600052602260045260246000fd5b90600160028304921680156104ec575b60208310146104e757565b6104b6565b91607f16916104dc565b9160001960089290920291821b911b61027f565b919061051b6101ba6102c6936101de565b9083546104f6565b61014a9160009161050a565b81811061053a575050565b806105486000600193610523565b0161052f565b9190601f811161055d57505050565b61056f61014a93600052602060002090565b906020601f840181900483019310610591575b6020601f90910104019061052f565b9091508190610582565b906105a4815190565b906001600160401b038211610131576105c7826105c185546104cc565b8561054e565b602090601f8311600114610602576102c69291600091836105f7575b5050600019600883021c1916906002021790565b0151905038806105e3565b601f1983169161061785600052602060002090565b9260005b8181106106555750916002939185600196941061063c575b50505002019055565b01516000196008601f8516021c19169055388080610633565b9193602060018192878701518155019501920161061b565b9061014a9161059b565b6106846101ba6101ba9290565b60ff1690565b634e487b7160e01b600052601160045260246000fd5b60ff16604d81116106b157600a0a90565b61068a565b818102929181159184041417156106b157565b6040513d6000823e3d90fd5b610734929161070661070d926106f36106ec610929565b600361049f565b6106ff6000600c61049f565b600061066d565b600161066d565b6107176012610677565b60805261072e61072960805160ff1690565b6106a0565b906106b6565b60a052604051605b81016001600160401b038111828210176101315761075f8291605b616687843990565b03906000f0801561078857610773906102a6565b60c0524660e0526107826109eb565b61010052565b6106c9565b905b600052602052604060002090565b906104af6101ba6102c69290565b906107de6107da6107d66107be85610a7a565b946101ba8560016107d084601061078d565b0161079d565b9390565b9190565b917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff61080960405190565b600090a4565b6101ba9061025f565b6101ba905461080f565b6108486108426108326011610818565b61083d8460116102af565b6102a6565b916102a6565b907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e061087360405190565b600090a3565b9061078f906102a6565b9060ff9061027f565b9061089c6101ba6102c692151590565b8254610883565b6108b46108b08383610aac565b1590565b15610922576108da60016108d58460006108cf86601061078d565b01610879565b61088c565b6108ee6108426108e8339390565b936102a6565b917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d61091960405190565b600090a4600190565b5050600090565b6101ba60016101de565b80546000939291610950610946836104cc565b8085529360200190565b91600181169081156109a2575060011461096957505050565b61097c9192939450600052602060002090565b916000925b81841061098e5750500190565b805484840152602090930192600101610981565b92949550505060ff1916825215156020020190565b906101ba91610933565b9061014a6109db926109d260405190565b938480926109b7565b0383610110565b6101ba906109c1565b6103bd610a5a6109fb60006109e2565b610a06610368825190565b20610a10306102a6565b90610a1a60405190565b93849260208401927fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc646916000805160206166e2833981519152866102f6565b610a65610368825190565b2090565b6101ba9081565b6101ba9054610a69565b6001610a936101ba92610a8b600090565b50601061078d565b01610a70565b6101ba90610684565b6101ba9054610a99565b6101ba9160006108cf610ac193610a8b600090565b610aa256fe60806040526004361015610020575b361561001657005b61001e613215565b005b60003560e01c8062f714ce1461041f57806301ffc9a71461041a578063058985de1461041557806306fdde0314610410578063081812fc1461040b578063095ea7b31461040657806314d0ae661461040157806318160ddd146103fc57806323b872dd146103f7578063248a9ca3146103f25780632f2ff15d146103ed578063313ce567146103e857806334b02e8e146103e35780633644e515146103de578063364f5d23146103d957806336568abe146103d4578063412e5f49146103cf57806342842e0e146103ca57806342966c68146103c55780634443f0a4146103c057806344a5bfa8146103bb5780634f558e79146103b65780634f8a2737146103b157806355f804b3146103ac5780635b21186d146103a75780635e56b06a146103a25780636315ab461461039d5780636352211e1461039857806364aae4161461039357806370a082311461038e578063715018a61461038957806373f425611461038457806375794a3c1461037f5780637e1c0c091461037a5780637e3c9599146103755780637ecebe00146103705780638adae3261461036b5780638da5cb5b14610366578063907af6c0146103615780639198e19a1461035c57806391d148541461035757806395d89b411461035257806396a8ef9e1461034d578063a217fddf14610348578063a22cb46514610343578063a9059cbb1461033e578063aa97eca214610339578063ade474e614610334578063b1ab93171461032f578063b88d4fde1461032a578063be00df4a14610325578063c87b56dd14610320578063d505accf1461031b578063d539139314610316578063d547741f14610311578063dab400f31461030c578063dd62ed3e14610307578063e0d4ea3714610302578063e985e9c5146102fd578063ecb41c75146102f8578063ed915ea1146102f3578063f2fde38b146102ee578063fcf8be1f146102e95763fd89b0f50361000e5761135c565b611340565b611328565b611314565b6112db565b6112bf565b6112a4565b611288565b611233565b61121a565b6111e1565b6111c2565b611132565b61111a565b6110fe565b611094565b611079565b61105e565b611042565b611029565b61100e565b610fe8565b610fa4565b610f88565b610f6d565b610f34565b610f19565b610ee0565b610ec5565b610e51565b610e13565b610df8565b610ddd565b610dc5565b610daa565b610d7a565b610d5f565b610d46565b610d2b565b610d12565b610cbb565b610ca3565b610bdd565b610bc1565b610b16565b610a57565b610a3e565b610a23565b610a0a565b6109d1565b6109b6565b61099b565b6107cc565b6107b3565b610798565b61077c565b610728565b610705565b610659565b610601565b6105c6565b610537565b6104e1565b61048f565b805b0361042d57565b600080fd5b9050359061043f82610424565b565b6001600160a01b031690565b90565b6001600160a01b038116610426565b9050359061043f82610450565b919060408382031261042d5761044d9060206104888286610432565b940161045f565b6104a361049d36600461046c565b9061147f565b604051005b0390f35b6001600160e01b03198116610426565b9050359061043f826104ac565b9060208282031261042d5761044d916104bc565b9052565b3461042d576104a86104fc6104f73660046104c9565b611489565b60405191829182901515815260200190565b801515610426565b9050359061043f8261050e565b9060208282031261042d5761044d91610516565b3461042d576104a361054a366004610523565b611492565b600091031261042d57565b60005b83811061056d5750506000910152565b818101518382015260200161055d565b61059e6105a76020936105b193610592815190565b80835293849260200190565b9586910161055a565b601f01601f191690565b0190565b602080825261044d9291019061057d565b3461042d576105d636600461054f565b6104a86105e16114a5565b604051918291826105b5565b9060208282031261042d5761044d91610432565b3461042d576104a861061c6106173660046105ed565b6114c2565b604051918291826001600160a01b03909116815260200190565b919060408382031261042d5761044d906020610652828661045f565b9401610432565b3461042d576104a86104fc61066f366004610636565b90611510565b909160608284031261042d5761044d61068e848461045f565b9360406106528260208701610432565b906106be6106b76106ad845190565b8084529260200190565b9260200190565b9060005b8181106106cf5750505090565b9091926106ec6106e56001928651815260200190565b9460200190565b9291016106c2565b602080825261044d9291019061069e565b6104a861071c610716366004610675565b91611721565b604051918291826106f4565b3461042d5761073836600461054f565b6104a8610743611741565b6040519182918290815260200190565b909160608284031261042d5761044d61076c848461045f565b936040610652826020870161045f565b3461042d576104a86104fc610792366004610753565b91611792565b3461042d576104a86107436107ae3660046105ed565b6117d6565b3461042d576104a36107c636600461046c565b90611813565b3461042d576107dc36600461054f565b6040517f000000000000000000000000000000000000000000000000000000000000000060ff168152602090f35b61044d61044d61044d9290565b906108219061080a565b600052602052604060002090565b634e487b7160e01b600052600060045260246000fd5b634e487b7160e01b600052602260045260246000fd5b906001600283049216801561087b575b602083101461087657565b610845565b91607f169161086b565b805460009392916108a26108988361085b565b8085529360200190565b91600181169081156108f457506001146108bb57505050565b6108ce9192939450600052602060002090565b916000925b8184106108e05750500190565b8054848401526020909301926001016108d3565b92949550505060ff1916825215156020020190565b9061044d91610885565b634e487b7160e01b600052604160045260246000fd5b90601f01601f191681019081106001600160401b0382111761094a57604052565b610913565b9061043f6109699261096060405190565b93848092610909565b0383610929565b906000106109815761044d9061094f565b61082f565b600061099661044d926016610817565b610970565b3461042d576104a86105e16109b13660046105ed565b610986565b3461042d576109c636600461054f565b6104a861074361181d565b3461042d576109e136600461054f565b6104a87fdb2819cfe36df1a8e2df5d8d9cfe49bf1b7c55ce41ab4723ae144d927ae5245b610743565b3461042d576104a3610a1d36600461046c565b90611870565b3461042d576104a86105e1610a393660046105ed565b611997565b3461042d576104a3610a51366004610753565b91611a38565b3461042d576104a3610a6a3660046105ed565b611a63565b909182601f8301121561042d578135916001600160401b03831161042d57602001926020830284011161042d57565b9060208282031261042d5781356001600160401b03811161042d57610ac39201610a6f565b9091565b90610ad66106b76106ad845190565b9060005b818110610ae75750505090565b909192610afd6106e56001928651815260200190565b929101610ada565b602080825261044d92910190610ac7565b3461042d576104a8610b32610b2c366004610a9e565b90611abb565b60405191829182610b05565b909182601f8301121561042d578135916001600160401b03831161042d57602001926001830284011161042d57565b60608183031261042d57610b818282610432565b9260208201356001600160401b03811161042d5783610ba1918401610b3e565b92909360408201356001600160401b03811161042d57610ac39201610b3e565b3461042d576104a3610bd4366004610b6d565b93929092611cbf565b3461042d576104a86104fc610bf33660046105ed565b611d10565b9061043f610c0560405190565b9283610929565b6001600160401b03811161094a57602090601f01601f19160190565b90826000939282370152565b90929192610c49610c4482610c0c565b610bf8565b938185528183011161042d5761043f916020850190610c28565b9080601f8301121561042d5781602061044d93359101610c34565b9060208282031261042d5781356001600160401b03811161042d5761044d9201610c63565b3461042d576104a3610cb6366004610c7e565b611e04565b3461042d576104a3610cce366004610c7e565b611e24565b6001600160801b038116610426565b9050359061043f82610cd3565b919060408382031261042d5761044d906020610d0b8286610432565b9401610ce2565b3461042d576104a3610d25366004610cef565b90611edd565b3461042d57610d3b36600461054f565b6104a861071c61207a565b3461042d576104a3610d59366004610636565b906120aa565b3461042d576104a861061c610d753660046105ed565b61213e565b3461042d576104a86104fc610d90366004610753565b916121f2565b9060208282031261042d5761044d9161045f565b3461042d576104a8610743610dc0366004610d96565b61233e565b3461042d57610dd536600461054f565b6104a3612372565b3461042d57610ded36600461054f565b6104a861074361237a565b3461042d57610e0836600461054f565b6104a8610743612382565b3461042d57610e2336600461054f565b6104a861074361238c565b919060408382031261042d5761044d906020610e4a828661045f565b9401610516565b3461042d576104a3610e64366004610e2e565b906123f2565b61044d90610441906001600160a01b031682565b61044d90610e6a565b61044d90610e7e565b9061082190610e87565b61044d916008021c81565b9061044d9154610e9a565b6000610ec061044d92600f610e90565b610ea5565b3461042d576104a8610743610edb366004610d96565b610eb0565b3461042d57610ef036600461054f565b6104a87f114ba586f27424819fdf1eac564faf22081abcab4f662a9e508c112ed1a7cbcf610743565b3461042d57610f2936600461054f565b6104a861061c6123fc565b3461042d57610f4436600461054f565b6104a87f0000000000000000000000000000000000000000000000000000000000000000610743565b3461042d57610f7d36600461054f565b6104a8610743612406565b3461042d576104a86104fc610f9e36600461046c565b90612425565b3461042d57610fb436600461054f565b6104a86105e1612445565b909160608284031261042d5761044d610fd8848461045f565b9360406104888260208701610432565b6104a3610ff6366004610fbf565b916125d8565b61044d600061080a565b61044d610ffc565b3461042d5761101e36600461054f565b6104a8610743611006565b3461042d576104a361103c366004610e2e565b90612603565b3461042d576104a86104fc611058366004610636565b90612699565b3461042d576104a86104fc611074366004610d96565b6126aa565b3461042d5761108936600461054f565b6104a86105e16126c1565b3461042d576104a861071c6110aa366004610d96565b612750565b9060808282031261042d576110c4818361045f565b926110d2826020850161045f565b926110e08360408301610432565b9260608201356001600160401b03811161042d5761044d9201610c63565b3461042d576104a36111113660046110af565b92919091612896565b3461042d576104a361112d366004610d96565b6128f4565b3461042d576104a86105e16111483660046105ed565b612966565b60ff8116610426565b9050359061043f8261114d565b60e08183031261042d57611177828261045f565b92611185836020840161045f565b926111938160408501610432565b926111a18260608301610432565b9261044d6111b28460808501611156565b9360c06106528260a08701610432565b3461042d576104a36111d5366004611163565b95949094939193612b35565b3461042d576111f136600461054f565b6104a87f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610743565b3461042d576104a361122d36600461046c565b90612dd6565b3461042d5761124336600461054f565b6104a87f0000000000000000000000000000000000000000000000000000000000000000610743565b919060408382031261042d5761044d906020610488828661045f565b3461042d576104a861074361129e36600461126c565b90612de0565b3461042d576104a86107436112ba3660046105ed565b612e0a565b3461042d576104a86104fc6112d536600461126c565b90612e68565b3461042d576112eb36600461054f565b6104a87f000000000000000000000000000000000000000000000000000000000000000061061c565b6104a3611322366004610fbf565b91613098565b3461042d576104a361133b366004610d96565b61310d565b3461042d576104a86104fc611356366004610636565b90613116565b3461042d576104a361136f366004610d96565b61320c565b9061043f91611381613250565b6113d8565b61044161044d61044d9290565b61044d90611386565b6104dd90610e87565b60208101929161043f919061139c565b90815260408101929161043f9160200152565b0152565b6040513d6000823e3d90fd5b6000916113e761044184611393565b6113f082610e87565b1461145c576113fe30610e87565b8031831161143257508280926114148293610e87565b828215611429575bf11561142457565b6113cc565b506108fc61141c565b9050319061145861144260405190565b631944d18360e21b8152928392600484016113b5565b0390fd5b6114589061146960405190565b639c86094560e01b8152918291600483016113a5565b9061043f91611374565b61044d906132a5565b61043f90336132ce565b61044d9061094f565b61044d600061149c565b61044d90610441565b61044d90546114af565b6114d26114ce8261338d565b1590565b6114e9576114e461044d916004610817565b6114b8565b611458906114f660405190565b637e27328960e01b81529182916004830190815260200190565b9061151a8161338d565b1561152d57611528916120aa565b600190565b61044d91613116565b61044d9392919061156b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a66133d0565b6133d0565b611650565b634e487b7160e01b600052601160045260246000fd5b9190611591565b9290565b820180921161159c57565b611570565b6001600160401b03811161094a5760208091020190565b906115c5610c44836115a1565b918252565b369037565b9061043f6115e56115df846115b8565b936115a1565b601f1901602084016115ca565b90600019905b9181191691161790565b9061161061044d6116179290565b82546115f2565b9055565b634e487b7160e01b600052603260045260246000fd5b9061163a825190565b81101561164b576020809102010190565b61161b565b50919261165d600061080a565b841461170f5761166b612382565b916116768584611586565b611689611685620208d561080a565b9190565b116116fd576116b5906116a58661169f816115cf565b966133db565b6116b0846017610817565b611602565b6116bf600061080a565b845b8110156116f6576116ef816116e96116dc6116c19487611586565b6116e68389611631565b52565b60010190565b90506116bf565b5092505090565b60405163318df65960e01b8152600490fd5b604051631758383360e11b8152600490fd5b61044d9291906060611536565b8181029291811591840414171561159c57565b61044d61174c6135a3565b7f00000000000000000000000000000000000000000000000000000000000000009061172e565b906115c5610c4483610c0c565b61044d6000611773565b61044d611780565b919061179d8261338d565b156117b6579061152892916117b061178a565b92612896565b61044d926121f2565b90610821565b61044d9081565b61044d90546117c5565b60016117ef61044d926117e7600090565b5060106117bf565b016117cc565b9061043f91611806611566826117d6565b90611810916135b6565b50565b9061043f916117f5565b7f00000000000000000000000000000000000000000000000000000000000000004603611868577f000000000000000000000000000000000000000000000000000000000000000090565b61044d61370d565b9061187a33610441565b6001600160a01b03821603611892576118109161379d565b60405163334bd91960e11b8152600490fd5b61044d905461085b565b61044d9081906001600160a01b031681565b805460009392916118d76118d38361085b565b9390565b916001811690811561192857506001146118f057505050565b6119039192939450600052602060002090565b6000905b8382106119145750500190565b600181602092548486015201910190611907565b60ff191683525050811515909102019150565b6105b16119539260209261194d815190565b94859290565b9384910161055a565b61197a611987936119746119749361044d97956118c0565b9061193b565b602f60f81b815260010190565b632e706e6760e01b815260040190565b6119ad6119a861044d836016610817565b6118a4565b6119ba611685600061080a565b11156119d3576119ce61044d916016610817565b61149c565b611a2c61044d61044d92611a12611a0c6119fc6119f76119f230610e87565b610e7e565b6118ae565b611a06601461080a565b9061385a565b916139be565b90611a1c60405190565b938492602084019260138461195c565b90810382520382610929565b909161043f926117b061178a565b6001600160a01b03909116815260408101929161043f9160200152565b611a706114ce8233613a51565b611a7d5761043f90613ab2565b61145890611a8a60405190565b63177e802f60e01b81529182913360048401611a46565b919081101561164b576020020190565b3561044d81610424565b91908092611ac8846115cf565b92611ad3600061080a565b855b811015611b0d57611b06816116e9611afc6112ba611af7611ad5968a8a611aa1565b611ab1565b6116e6838a611631565b9050611ad3565b509350505090565b91906105a781611b2c816105b19560209181520190565b8095610c28565b602080825261044d93910191611b15565b9160001960089290920291821b911b6115f8565b9190611b6961044d6116179361080a565b908354611b44565b61043f91600091611b58565b818110611b88575050565b80611b966000600193611b71565b01611b7d565b9190601f8111611bab57505050565b611bbd61043f93600052602060002090565b906020601f840181900483019310611bdf575b6020601f909101040190611b7d565b9091508190611bd0565b91906001600160401b03821161094a57611c0d82611c07855461085b565b85611b9c565b600090601f8311600114611c4857611617929160009183611c3d575b5050600019600883021c1916906002021790565b013590503880611c29565b90601f19831691611c5e85600052602060002090565b92825b818110611c9c57509160029391856001969410611c82575b50505002019055565b0135600019601f84166008021c19165b9055388080611c79565b92936020600181928786013581550195019301611c61565b9061043f9291611be9565b9493909192611cd46114ce838387878b613be2565b611cee575050611ce961043f93946016610817565b611cb4565b611458611cfa60405190565b631d9394a560e21b815292839260048401611b33565b61044d9061338d565b61043f90611d25613250565b611df9565b90611d33815190565b906001600160401b03821161094a57611d5082611c07855461085b565b602090601f8311600114611d8a57611617929160009183611d7f575050600019600883021c1916906002021790565b015190503880611c29565b601f19831691611d9f85600052602060002090565b9260005b818110611dd757509160029391856001969410611dc35750505002019055565b01516000196008601f8516021c1916611c92565b91936020600181928787015181550195019201611da3565b9061043f91611d2a565b61043f906014611def565b61043f90611d19565b61043f90611e19613250565b61043f906013611def565b61043f90611e0d565b61044d61044d61044d926001600160801b031690565b916001600160a01b0360089290920291821b911b6115f8565b9190611e6d61044d61161793610e87565b908354611e43565b61043f91600091611e5c565b805482101561164b57611e9b600191600052602060002090565b91020190600090565b90815491600160401b83101561094a5782611ec791600161043f95018155611e81565b90611b58565b9061161061044d6116179261080a565b90600d611ef2611eec83611e2d565b82613d41565b90611eff6114ce8561338d565b61206d57611f106114ce8533613a51565b61206057611fdd611fce611fca61200893611f76611fa089611f3181613d9e565b94829c929691611f4c6000611f47876004610817565b611e75565b60089685611f6c611f6684611f618d8d610e90565b611e81565b90610ea5565b60a01c928a613dcc565b611f8b6000611f86856009610817565b611b71565b611f9b83600a613e2d565b613e2d565b613e9e565b611fc56001600160a01b038816600160a01b01611fc061044d8585610e90565b611ea4565b610e90565b5490565b611fd8600161080a565b900390565b6001600160a01b03841660a09190911b6001600160a01b03191601612003836009610817565b611ecd565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169216908183600080516020615b6b833981519152600080a4600080516020615b6b833981519152600080a4565b61145884611a8a60405190565b611458846114f660405190565b61044d600d613f05565b906001600160a01b03906115f8565b906120a361044d61161792610e87565b8254612084565b6120b38261213e565b906001600160a01b0382163314158061212b575b612113576120df816120da856004610817565b612093565b6001600160a01b0390811691167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600080a4565b60405163a9fbf51f60e01b8152336004820152602490fd5b506121396114ce3384612e68565b6120c7565b61214a6114ce8261338d565b6114e95761216c61216561217192612160600a90565b614043565b6009610817565b6117cc565b6001600160a01b0316906121886104416000611393565b6001600160a01b0383161461219957565b7f00000000000000000000000000000000000000000000000000000000000000009150565b6001600160a01b03909116815260608101939261043f9290916040916113c8906020830152565b9190820391821161159c57565b9291906122026104416000611393565b6001600160a01b038216148015612303575b80156122e8575b6122b757336001600160a01b0385160361223a575b61044d9293614150565b61225261216c61224b866006610e90565b3390610e90565b936000198503612264575b9350612230565b8483116122925761228a61227c8461044d96976121e5565b61200361224b846006610e90565b84935061225d565b8490611458846122a160405190565b637dc7a0d960e11b8152938493600485016121be565b611458906122c460405190565b63ec442f0560e01b8152918291600483016001600160a01b03909116815260200190565b506001600160a01b0384166001600160a01b0382161461221b565b506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b03821614612214565b61216c61044d9161234d600090565b506002610e90565b61235d613250565b61043f61043f61236d6000611393565b6142fe565b61043f612355565b61044d61434a565b61044d60036117cc565b61044d6135a3565b9061043f916123a1613250565b9190823b6123b2611685600061080a565b146123c15761043f91926132ce565b611458836123ce60405190565b639c86094560e01b8152918291600483016001600160a01b03909116815260200190565b9061043f91612394565b61044d60116114b8565b61044d600d614354565b61044d905b60ff1690565b61044d9054612410565b61044d91600061243a612440936117e7600090565b01610e90565b61241b565b61044d600161149c565b9061043f929161245d613250565b6124a7565b9050519061043f82610450565b9060208282031261042d5761044d91612462565b6001600160a01b0391821681529116602082015260608101929161043f9160400152565b6124b090610e87565b906124be6104416000611393565b6001600160a01b038416146123c1576124df6124d930610e87565b92610e87565b6040516331a9108f60e11b815260048101839052602081602481855afa80156114245761251b916000916125a9575b506001600160a01b031690565b6001600160a01b0384160361258557803b1561042d5761255c936000809461254260405190565b96879586948593632142170760e11b855260048501612483565b03925af180156114245761256d5750565b61043f90600061257d8183610929565b81019061054f565b509061145861259360405190565b633f82dd3d60e11b815292839260048401611a46565b6125cb915060203d6020116125d1575b6125c38183610929565b81019061246f565b3861250e565b503d6125b9565b9061043f929161244f565b9060ff906115f8565b906125fc61044d61161792151590565b82546125e3565b90336001600160a01b038316146126685761262c8161262784611fc5336005610e90565b6125ec565b60009081526001600160a01b03919091169033907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3190602090a3565b6114588261267560405190565b630b61174360e31b8152918291600483016001600160a01b03909116815260200190565b6126a4919033611792565b50600190565b61244061044d916126b9600090565b506007610e90565b61044d601461149c565b906126e66126da6106ad845490565b92600052602060002090565b9060005b8181106126f75750505090565b90919261271b61271460019261270c876117cc565b815260200190565b9460010190565b9291016126ea565b9061044d916126cb565b9061043f6109699261273e60405190565b93848092612723565b61044d9061272d565b906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b0383161461288c5760006127958161080a565b926127ab6127a66000926008610e90565b612747565b91845b6127b961044d855190565b8610156127e0576001906127d46127d08887611631565b5190565b60a01c019501946127ae565b6127ee9193949295506115cf565b9182946127fa8261080a565b905080915b61280a61044d875190565b8310156128845761281e6127d08488611631565b60a081901c966001600160a01b03909116949061283a8361080a565b935b8885101561286757612714612861916116e9612857888b0190565b6116e6838d611631565b9361283c565b9893509390945061287a91965060010190565b91949590956127ff565b505050915050565b905061044d61207a565b939291906128a66114ce8361338d565b6128d2576128b76114ce8333613a51565b6128c55761043f93946143d2565b61145882611a8a60405190565b611458826114f660405190565b61043f906128eb613250565b61043f906144d6565b61043f906128df565b9092919261290d610c4482610c0c565b938185528183011161042d5761043f91602085019061055a565b9080601f8301121561042d57815161044d926020016128fd565b9060208282031261042d5781516001600160401b03811161042d5761044d9201612927565b60006129ae91612974606090565b5061298d61298861298861298860156114b8565b610e87565b6040519384928391829163c87b56dd60e01b83526004830190815260200190565b03915afa908115611424576000916129c4575090565b61044d91503d806000833e6129d98183610929565b810190612941565b949290979695939160e086019860008701612a03916001600160a01b03169052565b6001600160a01b031660208601526040850152606084015260ff16608083015260a082015260c00152565b9194612a7d6113c892989795612a7660a096612a6661043f9a612a5660c08a019e60008b0152565b6001600160a01b03166020890152565b6001600160a01b03166040870152565b6060850152565b6080830152565b6020809392612aa16115c56105b19461190160f01b815260020190565b01918252565b6113c861043f94612ad0606094989795612ac6608086019a6000870152565b60ff166020850152565b6040830152565b9593919897969492909861010087019960008801612afc916001600160a01b03169052565b6001600160a01b031660208701526001600160a01b031660408601526060850152608084015260ff1660a083015260c082015260e00152565b95909394929192612b434290565b8310612d9557612b528461338d565b612d6e57600095612b6287611393565b936001600160a01b0385166001600160a01b038816148015612d33575b612d025760208883611a2c612c1e8d8b612bf48e612b9b61181d565b93611a2c8b612bab83600f610e90565b612bc3612bb7826117cc565b91600183015b90611ecd565b6040519687958e8701957f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c987612a2e565b612c06612bff825190565b9160200190565b2090612c1160405190565b9384928884019283612a84565b612c29612bff825190565b20612c418888612c3860405190565b94859485612aa7565b838052039060015afa15611424578751946001600160a01b03166001600160a01b038616148015612ce6575b612cc057505050509181612cad60209361200384611fc57f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925986006610e90565b84526001600160a01b03908116941692a3565b61145894958997612cd060405190565b63d01a799560e01b815298899860048a01612ad7565b506001600160a01b0389166001600160a01b0386161415612c6d565b61145887612d0f60405190565b634a1406b160e11b8152918291600483016001600160a01b03909116815260200190565b506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b03881614612b7f565b61145884612d7b60405190565b635dbad48560e01b81529182916004830190815260200190565b85611458938895612da560405190565b63dc99de6360e01b8152978897600489016129e1565b9061043f91612dcc611566826117d6565b906118109161379d565b9061043f91612dbb565b61044d91611fc561216c92612df3600090565b506006610e90565b600019811461159c5760010190565b90600090612e18600061080a565b612e22600061080a565b905b612e2d8461080a565b8103612e605750612e2d612e58612e5261216c6017612e4c868a6121e5565b90610817565b92612dfb565b919050612e24565b925050915090565b61044d91611fc561244092612e7b600090565b506005610e90565b9061043f9291612e91613250565b612ef3565b9050519061043f82610424565b9060208282031261042d5761044d91612e96565b9050519061043f8261050e565b9060208282031261042d5761044d91612eb7565b91602061043f9294936113c86040820196600083019061139c565b612eff90929192610e87565b612f0c6104416000611393565b612f1583610e87565b1461308b57612f2390610e87565b906370a08231612f3230610e87565b90612f3c60405190565b612f468260e01b90565b81526001600160a01b0383166004820152602081602481885afa801561142457612f7591600091613072575090565b8511612fe857505091602091612faf936000612f9060405190565b809681958294612fa463a9059cbb60e01b90565b845260048401612ed8565b03925af1801561142457612fc05750565b6118109060203d602011612fe1575b612fd98183610929565b810190612ec4565b503d612fcf565b6020613021928561300a8894612ffd60405190565b9687948593849360e01b90565b83526001600160a01b031660048301526024820190565b03915afa91821561142457600092613041575b5061145861144260405190565b61306491925060203d60201161306b575b61305c8183610929565b810190612ea3565b9082613034565b503d613052565b61044d915060203d60201161306b5761305c8183610929565b6114588261146960405190565b9061043f9291612e83565b61043f906130af613250565b6130b96000611393565b6001600160a01b0381166001600160a01b038316146130dc575061043f906142fe565b611458906130e960405190565b631e4fbdf760e01b8152918291600483016001600160a01b03909116815260200190565b61043f906130a3565b906131246104416000611393565b6001600160a01b038316148015613199575b61318c5761314d8161200384611fc5336006610e90565b60009081526001600160a01b03919091169033907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3600190565b61145882612d0f60405190565b506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b03831614613136565b61043f906132017fdb2819cfe36df1a8e2df5d8d9cfe49bf1b7c55ce41ab4723ae144d927ae5245b6133d0565b61043f906015612093565b61043f906131d4565b7f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f8852587461323f60405190565b8061324b343383611a46565b0390a1565b6132586123fc565b339061326c825b916001600160a01b031690565b036132745750565b6114589061328160405190565b63118cdaa760e01b8152918291600483016001600160a01b03909116815260200190565b637965db0b60e01b6001600160e01b03198216149081156132c4575090565b61044d91506144e1565b91906132dd6104416000611393565b6001600160a01b038416148015613352575b6133215761262761043f9293826000146133135761330c81614640565b6007610e90565b61331c816144fb565b61330c565b6114588361332e60405190565b63024b6d7560e31b8152918291600483016001600160a01b03909116815260200190565b506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b038416146132ef565b61339881600b614752565b6133ca576133a961044d60036117cc565b811090816133b5575090565b90506133c561168561158d614794565b111590565b50600090565b61043f90339061479e565b6000906133e78261080a565b8314613591576133f961044183611393565b6001600160a01b038216148015613556575b6122b75761341960036117cc565b917f0000000000000000000000000000000000000000000000000000000000000000840261345861344b846002610e90565b612bbd836105b1836117cc565b8152838301906001600160a01b038316908181600080516020615b6b833981519152602082a361348c612440856007610e90565b156134bf5750505050816134ae61043f936134b8936134a9600a90565b6148af565b6105b160036117cc565b6003611ecd565b90919293806134d2600192611f96600a90565b6134dd8787836147d6565b61350a6134ee611fca886008610e90565b9661350384986134fd8661080a565b906121e5565b908361481f565b808484600080516020615b6b8339815191528180a4015b8381036135395750505050506134b861043f916134ae565b8085918484600080516020615b6b8339815191528180a401613521565b506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b0382161461340b565b604051636e45634760e11b8152600490fd5b61044d6135ae6149b4565b6134fd61434a565b6135c36114ce8383612425565b1561362c576135de600161262784600061243a8660106117bf565b6135f86135f26135ec339390565b93610e87565b91610e87565b917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d61362360405190565b600090a4600190565b5050600090565b805460009392916136466108988361085b565b91600181169081156108f4575060011461365f57505050565b6136729192939450600052602060002090565b916000925b8184106136845750500190565b805484840152602090930192600101613677565b9061044d91613633565b9061043f610969926136b360405190565b93848092613698565b61044d906136a2565b9095949261043f946136f76136fe926136f06080966136e960a088019c6000890152565b6020870152565b6040850152565b6060830152565b01906001600160a01b03169052565b611a2c61378e61371d60006136bc565b613728612bff825190565b2061373230610e87565b9061373c60405190565b93849260208401927fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc646917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f866136c5565b613799612bff825190565b2090565b6137a78282612425565b1561362c576137c16000612627848261243a8660106117bf565b6137cf6135f26135ec339390565b917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b61362360405190565b9061043f6115e561380a84611773565b93610c0c565b90613819825190565b81101561164b570160200190565b801561159c576000190190565b61241561044d61044d9290565b61044d9061385561168561044d9460ff1690565b901c90565b908161388a61388561387584613870600261080a565b61172e565b61387f600261080a565b90611586565b6137fa565b92600060306138a161389b8361080a565b87613810565b536001600f60fb1b821a6138bd6138b78361080a565b88613810565b53806138d96138d087613870600261080a565b61387f8361080a565b915b61391a575b50506116856138ee9161080a565b036138f857505090565b61145861390460405190565b63e22e27eb60e01b8152928392600484016113b5565b90926139258261080a565b841115613993576f181899199a1a9b1b9c1cb0b131b232b360811b61394a600f61080a565b821690601082101561164b57839261396a6139879261398d941a60f81b90565b861a613976888c613810565b536139816004613834565b90613841565b94613827565b916138db565b926138e0565b634e487b7160e01b600052601260045260246000fd5b81156139b9570490565b613999565b6139c7816149c9565b906139d86001926105b1600161080a565b91806139e3846137fa565b936020018401905b6139f6575b50505090565b8115613a4c57613a309060001901926f181899199a1a9b1b9c1cb0b131b232b360811b600a82061a8453613a2a600a61080a565b906139af565b9081613a3f611685600061080a565b14613a4c579091816139eb565b6139f0565b613a5a8261213e565b6001600160a01b0381166001600160a01b03831614928315613a8d575b508215613a8357505090565b61044d9250612e68565b613a989193506114c2565b613aaa6001600160a01b03831661325f565b149138613a77565b613abe6114ce8261338d565b6114e9576000613aff613ad083613d9e565b613ae285611f47886004979697610817565b85613af5611f6684611f61886008610e90565b60a01c9285613dcc565b613b0e82611f86856009610817565b613b1983600a613e2d565b613b2483600b613e2d565b7f000000000000000000000000000000000000000000000000000000000000000090613b65613b54826002610e90565b612bbd84613b61836117cc565b0390565b613b7c613b756116e9600c6117cc565b600c611ecd565b9082526001600160a01b03168181600080516020615b6b833981519152602083a3600080516020615b6b8339815191528280a4565b61044d913691610c34565b6136fe61043f94612ad0606094989795613bdb608086019a6000870152565b6020850152565b9390613bee60126114b8565b613bfe61325f6104416000611393565b14613ce757613cbb613ccd93611a2c92613c97613cd398613c6e613c687f000000000000000000000000000000000000000000000000000000000000000095613c647f114ba586f27424819fdf1eac564faf22081abcab4f662a9e508c112ed1a7cbcf90565b9591565b90613bb1565b613c79612bff825190565b2090611a2c613c8760405190565b9485936020850193339285613bbc565b613ca2612bff825190565b2090613cad60405190565b938492602084019283612a84565b613cc6612bff825190565b2092613bb1565b90614b6b565b613ce361325f61044160126114b8565b1490565b6040516395e0712b60e01b8152600490fd5b61044d905b6001600160801b031690565b61044d9054613cf9565b613cfe61044d61044d9290565b613cfe61044d61044d926001600160801b031690565b9061082190613d21565b90613d4e61044d83614354565b811015613d8c5781613d8661216c92613d79613d736000600161044d98019501613d0a565b91613d14565b016001600160801b031690565b90613d37565b60405163580821e760e01b8152600490fd5b613da990600a614043565b613db761216c826009610817565b6001600160a01b0381169260a09190911c9190565b93919092613dd78290565b8103613e045750613de8600161080a565b8203613df957505061043f91614d2b565b909161043f93614c78565b919261043f94614b81565b61044d600160ff1b61080a565b61044d9061385561168561044d9490565b613e6e61043f926000613e67613e4c613e466008613834565b84613841565b92613e55613e0f565b90613e6060ff61080a565b1690613e1c565b9301610817565b90613e78826117cc565b1790611ecd565b6001600160801b03908116911601906001600160801b03821161159c57565b9190613eac61044d84614354565b613eb582611e2d565b1015613d8c5782613d8661200392613ed66000600161043f98019401613d0a565b90613e7f565b61044d9060801c613cfe565b61044d9054613edc565b600161044d91016001600160801b031690565b613f1d613f2a82613f1581613d0a565b928391613ee8565b036001600160801b031690565b91613f3c613f3784611e2d565b6115cf565b938490613f496000613d14565b6001600160801b0386165b6001600160801b0382161015613fa857613fa1613f5491613f9c613f8a61216c60018901613d86858c016001600160801b031690565b6116e6613f9684611e2d565b8c611631565b613ef2565b9050613f49565b50935093505050565b15613fb857565b60405162461bcd60e51b815260206004820152603460248201527f4269744d6170733a205468652073657420626974206265666f7265207468652060448201527334b73232bc103237b2b9b713ba1032bc34b9ba1760611b6064820152608490fd5b61044d9061402e61168561044d9460ff1690565b901b90565b61044d61044d61044d9260ff1690565b6000916140506008613834565b9061405b8282613841565b92839161408e61407c61216c60ff9361407460ff61080a565b169785610817565b8661408760ff61080a565b1890613e1c565b9081966000926140a06116858561080a565b11156140cd57505050506140c46140be6140c99493611fd89361401a565b94614f3e565b614033565b1790565b92979650929094505b6140f4906140ed6140e68761080a565b8211613fb1565b6000190190565b908161410561216c82888b01610817565b9061410f8761080a565b821161411d575050906140d6565b61413d919798506140c9965061414993506141439250946140c49561401a565b95614f3e565b91613834565b0360ff1690565b9160029261416161216c8286610e90565b61416e61216c8587610e90565b928082106142da576141906141838488610e90565b612bbd83613b61836117cc565b61419d61344b8688610e90565b60008181526001600160a01b038681169190851690600080516020615b6b83398151915290602090a36141d4612440846007610e90565b6141e2612440876007610e90565b9080806142d3575b156141fd575b5050505050505050600190565b15614261575050505061424e90611fd861421d61216c8561425497610e90565b6142487f000000000000000000000000000000000000000000000000000000000000000080926139af565b926139af565b90615361565b38808080808080806141f0565b949092949391936000146142c5575050506142c092611fd86142ba926142b561216c866142af7f000000000000000000000000000000000000000000000000000000000000000080956139af565b95610e90565b6139af565b906151e4565b614254565b909291936142c09550614f97565b50816141ea565b61145883916142e860405190565b63391434e360e21b8152938493600485016121be565b6143196135f261430e60116114b8565b612988846011612093565b907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e061434460405190565b600090a3565b61044d600c6117cc565b614387600061044d92614365600090565b500161437961437382613ee8565b91613d0a565b90036001600160801b031690565b611e2d565b6104dd9061080a565b6001600160a01b03909116815261044d93608082019390926143c591906143bb90613bdb565b604083019061438c565b606081840391015261057d565b9093926143ee6114ce826143e6600161080a565b86898761541b565b6144af57506144006104416000611393565b6001600160a01b038516148015614474575b8015614459575b614428579261043f9293615585565b6114588461443560405190565b633250574960e11b8152918291600483016001600160a01b03909116815260200190565b506001600160a01b0381166001600160a01b03851614614419565b506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166001600160a01b03851614614412565b84611458849260016144c060405190565b633f756b1760e01b815294859460048601614395565b61043f906012612093565b613ce36301ffc9a760e01b5b916001600160e01b03191690565b61453361450c61216c836002610e90565b7f0000000000000000000000000000000000000000000000000000000000000000906139af565b9061453e600061080a565b9061454d6127a6826008610e90565b93825b61455b61044d875190565b84101561457e576001906145726127d08689611631565b60a01c01930192614550565b9194509203905090614590600061080a565b825b8110156145b0576145a9614592916116e9866156fa565b9050614590565b50915050565b8181106145c1575050565b806145cf6000600193611b71565b016145b6565b9190918282106145e457505050565b61043f92916145f99091600052602060002090565b91820191016145b6565b90600160401b811161094a578161461b61043f935490565b908281556145d5565b600061043f91614603565b906000036109815761043f90614624565b9061464f6127a6836008610e90565b9060009061465c8261080a565b905b61466961044d855190565b82101561473b57906146a29161467f8287615778565b61469186611f86846009989598610817565b61469d8186600a6148af565b840190565b947f0000000000000000000000000000000000000000000000000000000000000000935b8681101561471d57806146e187611f47614718946004610817565b6146f181600d6157cd565b6157cd565b806001600160a01b03878116908b16600080516020615b6b8339815191528a80a460010190565b6146c6565b509450915061044d6147326146699260010190565b9291505061465e565b9361043f935061474d91506008610e90565b61462f565b9061477f61216c61478f92614765600090565b506000614778613e4c613e466008613834565b9501610817565b9161478a600061080a565b921690565b141590565b61044d600161080a565b906147ac6114ce8284612425565b6147b4575050565b6114586147c060405190565b63e2517d3f60e01b815292839260048401611a46565b61044d611fc09161043f946147e9600090565b506001600160a01b0390911660a09190911b6001600160a01b03191601926008610e90565b61044d6001600160a01b031961080a565b9092919261483b61044d61483161480e565b61398160a0613834565b8411614877576120039061043f9394614852600090565b506001600160a01b0390911660a09190911b6001600160a01b03191601916009610817565b6114588461488460405190565b631605cc3f60e11b81529182916004830190815260200190565b61044d9061402e61168561044d9490565b916148c36148bd6008613834565b83613841565b80926148cf60ff61080a565b1691610100918184016148e46116858561080a565b101561491e5750613e67613e6e9361491961043f979694614913600095613b6161490d60001990565b9361080a565b9061489e565b613e1c565b959491935061495e9061495790611fd86000199561494e6000614941838a613e1c565b970196613e6e8c89610817565b613b618861080a565b9560010190565b945b6149698461080a565b8111156149945761495761498e91614985856120038a87610817565b611fd88661080a565b94614960565b613e6e926149136149ae92613b6161043f9899959761080a565b92610817565b61044d6149c160036117cc565b6134fd614794565b6149d3600061080a565b907a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000006149f98161080a565b821015614b49575b506d04ee2d6d415b85acef8100000000614a1a8161080a565b821015614b27575b50662386f26fc10000614a348161080a565b821015614b05575b506305f5e100614a4b8161080a565b821015614ae3575b50612710614a608161080a565b821015614ac1575b50614a73606461080a565b811015614a9f575b614a88611685600a61080a565b1015614a915790565b61044d906105b1600161080a565b614ab0614abb91613a2a606461080a565b916105b1600261080a565b90614a7b565b614adc91613a2a614ad19261080a565b916105b1600461080a565b9038614a68565b614afe91613a2a614af39261080a565b916105b1600861080a565b9038614a53565b614b2091613a2a614b159261080a565b916105b1601061080a565b9038614a3c565b614b4291613a2a614b379261080a565b916105b1602061080a565b9038614a22565b614b6491613a2a614b599261080a565b916105b1604061080a565b9038614a01565b61044d91614b789161582b565b909291926158cb565b909392600a93614b918486613e2d565b614bc5828503926008976001600160a01b0390911660a085901b6001600160a01b0319160190611ec790611f61878b610e90565b614bd4600191611fd88361080a565b95868310614be6575b50505050505050565b614c42611fca85611fd893611fc5614c1a614c4898614c6c9d613b61614c126120039e6105b18d61080a565b9e8f90613e2d565b6001600160a01b038c1660a09190911b6001600160a01b03191601611fc061044d8585610e90565b9161080a565b6001600160a01b0390911660a09190911b6001600160a01b03191601916009610817565b38808080808080614bdd565b9190611f6161043f94614c92611ec7946105b1600161080a565b90614c9e82600a613e2d565b614cc66001600160a01b03871660a086901b6001600160a01b03191601612003846009610817565b6001600160a01b0390911660001990910160a01b6001600160a01b03191601936008610e90565b634e487b7160e01b600052603160045260246000fd5b80548015614d26576000190190614d23614d1d8383611e81565b90611b71565b55565b614ced565b9061043f91614d3e61044d826008610e90565b91614d4a611fce845490565b818103614d5a575b505050614d03565b614da492614d6e611f666120039387611e81565b614d7c81611ec78689611e81565b6001600160a01b0391821660a09490941b6001600160a01b0319169390930192166009610817565b388080614d52565b614db7610100611773565b7e01020903110a19042112290b311a3905412245134d2a550c5d32651b6d3a7560208201527f06264262237d468514804e8d2b95569d0d495ea533a966b11c886eb93bc176c960408201527f071727374353637324837e9b47af86c7155181ad4fd18ed32c9096db57d59ee360608201527f0e2e4a6a5f92a6be3498aae067ddb2eb1d5989b56fd7baf33ca0c2ee77e5caf760808201527fff0810182028303840444c545c646c7425617c847f8c949c48a4a8b087b8c0c860a08201527f16365272829aaec650acd0d28fdad4e22d6991bd97dfdcea58b4d6f29fede4f660c08201527ffe0f1f2f3f4b5b6b607b8b93a3a7b7bf357199c5abcfd9e168bcdee9b3f1ecf560e08201527ffd1e3e5a7a8aa2b670c4ced8bbe8f0f4fc3d79a1c3cde7effb78cce6facbf9f861010082015290565b61044d614dac565b61044d614eea565b61044d7e818283848586878898a8b8c8d8e8f929395969799a9b9d9e9faaeb6bedeeff61080a565b61241561044d61044d9260ff1690565b61044d9060f81c614f22565b614f92614f8461044d92614f50600090565b50614f7e614f74614f68614f62614ef2565b9361599b565b614f70614efa565b0290565b61398160f8613834565b90613810565b516001600160f81b03191690565b614f32565b909194614fa2600090565b50614fce7f000000000000000000000000000000000000000000000000000000000000000080966139af565b93600095614fdc600061080a565b86811015615173579786979860089795969790614ffc611fca8984610e90565b918961500d6001946134fd8661080a565b918a6150198482615778565b94909b61502c61168561158d8989611586565b1161510b57615083611fca8585611fc561507e61044d615093986150788f9d9b99615072611f6661509b9f61044d6150679161508c9f610e90565b92611f61868b610e90565b90611ea4565b84610e90565b614d03565b6134fd8661080a565b8d8c61481f565b898101930190565b9789918d915b905b6150b7575b50505050509796959493614fdc565b8b8482101561510557859383600080516020615b6b83398151915284936150e66150fc97611f47876004610817565b6001600160a01b0390811693169180a460010190565b8c908a926150a3565b506150a8565b9250509261511b908d9a939a0390565b98899003908161512c918c856159c1565b019061513982600a613e2d565b896151458982856147d6565b61514e91610e90565b546151588361080a565b61516491038a8361481f565b958601818a9789918d916150a1565b506151bf94965080611685946151a5615193611fd89461158d979c6139af565b611fd8846142b561216c866002610e90565b88106151d0575b50614248816142b561216c8b6002610e90565b116151c75750565b61043f906156fa565b6151de906142ba600161080a565b386151ac565b91906000926151f3600061080a565b925b8284101561535a5760086152098382610e90565b90615233611f6661521d611fca8785610e90565b9361522d600195611fd88761080a565b90611e81565b60018060a01b0381169060a01c87868a61524d84809c0190565b8a106153145761507e61044d61528295949387986152766105b195611f866152889b6009610817565b611fc5878b600a6148af565b96820190565b7f000000000000000000000000000000000000000000000000000000000000000091835b6152ba575b505050506151f5565b8181101561530f57615309816152d386936146ec600d90565b6152e28b611f47836004610817565b806001600160a01b03868116908a16600080516020615b6b8339815191528e80a460010190565b906152ac565b6152b1565b50508703905096879003858282019381858b81019b61533491600a6148af565b61533d91610e90565b546153478661080a565b900390615353936159c1565b8495615288565b9350505050565b9161536c600061080a565b825b8110156145b05761538561536e916116e9866156fa565b905061536c565b9050519061043f826104ac565b9060208282031261042d5761044d9161538c565b6001600160a01b0391821681529116602082015261044d9260808201929091906143c590612ad0565b3d156153f0576153e53d611773565b903d6000602084013e565b606090565b6001600160a01b03909116815261044d93608082019390926143c59190612ad090613bdb565b93919291600092823b6154306116858661080a565b111561557a576001936001968587905b615450575b505050505050505090565b61545d61044d858a611586565b8110156155755761547061298887610e87565b602061547b60405190565b918290630a85bd0160e11b825281878161549b8c898b33600486016153ad565b03925af160009181615545575b506155155750866154c1578691906001015b9091615440565b50508594506154ce6153d6565b906154e36116856154dd845190565b9261080a565b0361550d575090611458916154f760405190565b633f756b1760e01b8152948594600486016153f5565b805190602001fd5b896154ba918994939b9161552c575b509960010190565b905061553e630a85bd0160e11b6144ed565b1438615524565b61556791925060203d811161556e575b61555f8183610929565b810190615399565b90386154a8565b503d615555565b615445565b505050505050600190565b91816155c461559383613d9e565b6155a66000611f47886004979697610817565b856155ba611f6684611f6160089889610e90565b60a01c9289613dcc565b6155d2612440836007610e90565b1561569a5750506155e581611f96600a90565b6155f081600d6157cd565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116908416600080516020615b6b833981519152600080a45b7f0000000000000000000000000000000000000000000000000000000000000000615661614183846002610e90565b61566f61344b836002610e90565b60009081526001600160a01b039182169290911690600080516020615b6b83398151915290602090a3565b6156d491611fdd916156ca91611fca91611fc56001600160a01b038816600160a01b01611fc061044d8585610e90565b6134fd600161080a565b6001600160a01b03828116908416600080516020615b6b833981519152600080a4615632565b615704600d615a41565b9061573561572e615715600161080a565b6157208185876147d6565b613b61611fca856008610e90565b828461481f565b6001600160a01b03908116907f000000000000000000000000000000000000000000000000000000000000000016600080516020615b6b833981519152600080a4565b61579391611f61611f669261578b600090565b506008610e90565b6001600160a01b0381169160a09190911c90565b906001600160801b03906115f8565b906157c661044d61161792613d21565b82546157a7565b806157e46157da82613d0a565b6143796001613d14565b916157f1613cfe83613ee8565b6001600160801b038416146158195761043f936120038460016158149401613d37565b6157b6565b604051638acb5f2760e01b8152600490fd5b90600091615837825190565b615844611685604161080a565b0361586e5761586792506020820151906060604084015193015160001a90615aac565b9192909190565b50905061588b6158866158816000611393565b925190565b61080a565b909160029190565b634e487b7160e01b600052602160045260246000fd5b600411156158b357565b615893565b9061043f826158a9565b61044d9061080a565b6158d560006158b8565b6158de826158b8565b036158e7575050565b6158f160016158b8565b6158fa826158b8565b036159115760405163f645eedf60e01b8152600490fd5b61591b60026158b8565b615924826158b8565b0361595257611458615935836158c2565b60405163fce698f760e01b81529182916004830190815260200190565b61596561595f60036158b8565b916158b8565b1461596d5750565b6114589061597a60405190565b6335e2f38360e21b81529182916004830190815260200190565b1561042d57565b6159af6159a8600061080a565b8211615994565b6159bd81613b61600061080a565b1690565b91611f61611ec7929361043f956159d6600090565b506001600160a01b0390911660a09190911b6001600160a01b03191601936008610e90565b600161044d91036001600160801b031690565b906fffffffffffffffffffffffffffffffff199060801b6115f8565b90615a3a61044d61161792613d21565b8254615a0e565b9081615a4c81613ee8565b90615a59613cfe85613d0a565b6001600160801b03831614615a9a576001615a7661043f936159fb565b910193615a956000611f8684615a8f61216c828b613d37565b98613d37565b615a2a565b6040516375e52f4f60e01b8152600490fd5b9091615ab7846158c2565b615ae36116857f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a061080a565b11615b565790615afc60209460009493612c3860405190565b838052039060015afa1561142457600051600091615b1983611393565b6001600160a01b0381166001600160a01b03841614615b425750615b3c8361080a565b91929190565b915091615b4e9061080a565b909160019190565b505050615b636000611393565b916003919056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212202701fb4ac6bfe60b7631cc1bea381b78d9c385162634a0e7a45801e33efeef5464736f6c63430008190033608060405234601657604051603f601c8239603f90f35b600080fdfe6080604052600080fdfea26469706673582212207e52eb64f3b00738a15edb69d7ae1fbdb79ee4d572f95c49d49da01ad9561f1664736f6c634300081900338b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400fdb2819cfe36df1a8e2df5d8d9cfe49bf1b7c55ce41ab4723ae144d927ae5245b

Deployed Bytecode

0x60806040526004361015610020575b361561001657005b61001e613215565b005b60003560e01c8062f714ce1461041f57806301ffc9a71461041a578063058985de1461041557806306fdde0314610410578063081812fc1461040b578063095ea7b31461040657806314d0ae661461040157806318160ddd146103fc57806323b872dd146103f7578063248a9ca3146103f25780632f2ff15d146103ed578063313ce567146103e857806334b02e8e146103e35780633644e515146103de578063364f5d23146103d957806336568abe146103d4578063412e5f49146103cf57806342842e0e146103ca57806342966c68146103c55780634443f0a4146103c057806344a5bfa8146103bb5780634f558e79146103b65780634f8a2737146103b157806355f804b3146103ac5780635b21186d146103a75780635e56b06a146103a25780636315ab461461039d5780636352211e1461039857806364aae4161461039357806370a082311461038e578063715018a61461038957806373f425611461038457806375794a3c1461037f5780637e1c0c091461037a5780637e3c9599146103755780637ecebe00146103705780638adae3261461036b5780638da5cb5b14610366578063907af6c0146103615780639198e19a1461035c57806391d148541461035757806395d89b411461035257806396a8ef9e1461034d578063a217fddf14610348578063a22cb46514610343578063a9059cbb1461033e578063aa97eca214610339578063ade474e614610334578063b1ab93171461032f578063b88d4fde1461032a578063be00df4a14610325578063c87b56dd14610320578063d505accf1461031b578063d539139314610316578063d547741f14610311578063dab400f31461030c578063dd62ed3e14610307578063e0d4ea3714610302578063e985e9c5146102fd578063ecb41c75146102f8578063ed915ea1146102f3578063f2fde38b146102ee578063fcf8be1f146102e95763fd89b0f50361000e5761135c565b611340565b611328565b611314565b6112db565b6112bf565b6112a4565b611288565b611233565b61121a565b6111e1565b6111c2565b611132565b61111a565b6110fe565b611094565b611079565b61105e565b611042565b611029565b61100e565b610fe8565b610fa4565b610f88565b610f6d565b610f34565b610f19565b610ee0565b610ec5565b610e51565b610e13565b610df8565b610ddd565b610dc5565b610daa565b610d7a565b610d5f565b610d46565b610d2b565b610d12565b610cbb565b610ca3565b610bdd565b610bc1565b610b16565b610a57565b610a3e565b610a23565b610a0a565b6109d1565b6109b6565b61099b565b6107cc565b6107b3565b610798565b61077c565b610728565b610705565b610659565b610601565b6105c6565b610537565b6104e1565b61048f565b805b0361042d57565b600080fd5b9050359061043f82610424565b565b6001600160a01b031690565b90565b6001600160a01b038116610426565b9050359061043f82610450565b919060408382031261042d5761044d9060206104888286610432565b940161045f565b6104a361049d36600461046c565b9061147f565b604051005b0390f35b6001600160e01b03198116610426565b9050359061043f826104ac565b9060208282031261042d5761044d916104bc565b9052565b3461042d576104a86104fc6104f73660046104c9565b611489565b60405191829182901515815260200190565b801515610426565b9050359061043f8261050e565b9060208282031261042d5761044d91610516565b3461042d576104a361054a366004610523565b611492565b600091031261042d57565b60005b83811061056d5750506000910152565b818101518382015260200161055d565b61059e6105a76020936105b193610592815190565b80835293849260200190565b9586910161055a565b601f01601f191690565b0190565b602080825261044d9291019061057d565b3461042d576105d636600461054f565b6104a86105e16114a5565b604051918291826105b5565b9060208282031261042d5761044d91610432565b3461042d576104a861061c6106173660046105ed565b6114c2565b604051918291826001600160a01b03909116815260200190565b919060408382031261042d5761044d906020610652828661045f565b9401610432565b3461042d576104a86104fc61066f366004610636565b90611510565b909160608284031261042d5761044d61068e848461045f565b9360406106528260208701610432565b906106be6106b76106ad845190565b8084529260200190565b9260200190565b9060005b8181106106cf5750505090565b9091926106ec6106e56001928651815260200190565b9460200190565b9291016106c2565b602080825261044d9291019061069e565b6104a861071c610716366004610675565b91611721565b604051918291826106f4565b3461042d5761073836600461054f565b6104a8610743611741565b6040519182918290815260200190565b909160608284031261042d5761044d61076c848461045f565b936040610652826020870161045f565b3461042d576104a86104fc610792366004610753565b91611792565b3461042d576104a86107436107ae3660046105ed565b6117d6565b3461042d576104a36107c636600461046c565b90611813565b3461042d576107dc36600461054f565b6040517f000000000000000000000000000000000000000000000000000000000000001260ff168152602090f35b61044d61044d61044d9290565b906108219061080a565b600052602052604060002090565b634e487b7160e01b600052600060045260246000fd5b634e487b7160e01b600052602260045260246000fd5b906001600283049216801561087b575b602083101461087657565b610845565b91607f169161086b565b805460009392916108a26108988361085b565b8085529360200190565b91600181169081156108f457506001146108bb57505050565b6108ce9192939450600052602060002090565b916000925b8184106108e05750500190565b8054848401526020909301926001016108d3565b92949550505060ff1916825215156020020190565b9061044d91610885565b634e487b7160e01b600052604160045260246000fd5b90601f01601f191681019081106001600160401b0382111761094a57604052565b610913565b9061043f6109699261096060405190565b93848092610909565b0383610929565b906000106109815761044d9061094f565b61082f565b600061099661044d926016610817565b610970565b3461042d576104a86105e16109b13660046105ed565b610986565b3461042d576109c636600461054f565b6104a861074361181d565b3461042d576109e136600461054f565b6104a87fdb2819cfe36df1a8e2df5d8d9cfe49bf1b7c55ce41ab4723ae144d927ae5245b610743565b3461042d576104a3610a1d36600461046c565b90611870565b3461042d576104a86105e1610a393660046105ed565b611997565b3461042d576104a3610a51366004610753565b91611a38565b3461042d576104a3610a6a3660046105ed565b611a63565b909182601f8301121561042d578135916001600160401b03831161042d57602001926020830284011161042d57565b9060208282031261042d5781356001600160401b03811161042d57610ac39201610a6f565b9091565b90610ad66106b76106ad845190565b9060005b818110610ae75750505090565b909192610afd6106e56001928651815260200190565b929101610ada565b602080825261044d92910190610ac7565b3461042d576104a8610b32610b2c366004610a9e565b90611abb565b60405191829182610b05565b909182601f8301121561042d578135916001600160401b03831161042d57602001926001830284011161042d57565b60608183031261042d57610b818282610432565b9260208201356001600160401b03811161042d5783610ba1918401610b3e565b92909360408201356001600160401b03811161042d57610ac39201610b3e565b3461042d576104a3610bd4366004610b6d565b93929092611cbf565b3461042d576104a86104fc610bf33660046105ed565b611d10565b9061043f610c0560405190565b9283610929565b6001600160401b03811161094a57602090601f01601f19160190565b90826000939282370152565b90929192610c49610c4482610c0c565b610bf8565b938185528183011161042d5761043f916020850190610c28565b9080601f8301121561042d5781602061044d93359101610c34565b9060208282031261042d5781356001600160401b03811161042d5761044d9201610c63565b3461042d576104a3610cb6366004610c7e565b611e04565b3461042d576104a3610cce366004610c7e565b611e24565b6001600160801b038116610426565b9050359061043f82610cd3565b919060408382031261042d5761044d906020610d0b8286610432565b9401610ce2565b3461042d576104a3610d25366004610cef565b90611edd565b3461042d57610d3b36600461054f565b6104a861071c61207a565b3461042d576104a3610d59366004610636565b906120aa565b3461042d576104a861061c610d753660046105ed565b61213e565b3461042d576104a86104fc610d90366004610753565b916121f2565b9060208282031261042d5761044d9161045f565b3461042d576104a8610743610dc0366004610d96565b61233e565b3461042d57610dd536600461054f565b6104a3612372565b3461042d57610ded36600461054f565b6104a861074361237a565b3461042d57610e0836600461054f565b6104a8610743612382565b3461042d57610e2336600461054f565b6104a861074361238c565b919060408382031261042d5761044d906020610e4a828661045f565b9401610516565b3461042d576104a3610e64366004610e2e565b906123f2565b61044d90610441906001600160a01b031682565b61044d90610e6a565b61044d90610e7e565b9061082190610e87565b61044d916008021c81565b9061044d9154610e9a565b6000610ec061044d92600f610e90565b610ea5565b3461042d576104a8610743610edb366004610d96565b610eb0565b3461042d57610ef036600461054f565b6104a87f114ba586f27424819fdf1eac564faf22081abcab4f662a9e508c112ed1a7cbcf610743565b3461042d57610f2936600461054f565b6104a861061c6123fc565b3461042d57610f4436600461054f565b6104a87f0000000000000000000000000000000000000000000000120d4da7b0bd140000610743565b3461042d57610f7d36600461054f565b6104a8610743612406565b3461042d576104a86104fc610f9e36600461046c565b90612425565b3461042d57610fb436600461054f565b6104a86105e1612445565b909160608284031261042d5761044d610fd8848461045f565b9360406104888260208701610432565b6104a3610ff6366004610fbf565b916125d8565b61044d600061080a565b61044d610ffc565b3461042d5761101e36600461054f565b6104a8610743611006565b3461042d576104a361103c366004610e2e565b90612603565b3461042d576104a86104fc611058366004610636565b90612699565b3461042d576104a86104fc611074366004610d96565b6126aa565b3461042d5761108936600461054f565b6104a86105e16126c1565b3461042d576104a861071c6110aa366004610d96565b612750565b9060808282031261042d576110c4818361045f565b926110d2826020850161045f565b926110e08360408301610432565b9260608201356001600160401b03811161042d5761044d9201610c63565b3461042d576104a36111113660046110af565b92919091612896565b3461042d576104a361112d366004610d96565b6128f4565b3461042d576104a86105e16111483660046105ed565b612966565b60ff8116610426565b9050359061043f8261114d565b60e08183031261042d57611177828261045f565b92611185836020840161045f565b926111938160408501610432565b926111a18260608301610432565b9261044d6111b28460808501611156565b9360c06106528260a08701610432565b3461042d576104a36111d5366004611163565b95949094939193612b35565b3461042d576111f136600461054f565b6104a87f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610743565b3461042d576104a361122d36600461046c565b90612dd6565b3461042d5761124336600461054f565b6104a87f0e7a552dfd1dbc578a80e6130b42ccb1e661a00a922ebfd0a0def6c5f5501fe4610743565b919060408382031261042d5761044d906020610488828661045f565b3461042d576104a861074361129e36600461126c565b90612de0565b3461042d576104a86107436112ba3660046105ed565b612e0a565b3461042d576104a86104fc6112d536600461126c565b90612e68565b3461042d576112eb36600461054f565b6104a87f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d61061c565b6104a3611322366004610fbf565b91613098565b3461042d576104a361133b366004610d96565b61310d565b3461042d576104a86104fc611356366004610636565b90613116565b3461042d576104a361136f366004610d96565b61320c565b9061043f91611381613250565b6113d8565b61044161044d61044d9290565b61044d90611386565b6104dd90610e87565b60208101929161043f919061139c565b90815260408101929161043f9160200152565b0152565b6040513d6000823e3d90fd5b6000916113e761044184611393565b6113f082610e87565b1461145c576113fe30610e87565b8031831161143257508280926114148293610e87565b828215611429575bf11561142457565b6113cc565b506108fc61141c565b9050319061145861144260405190565b631944d18360e21b8152928392600484016113b5565b0390fd5b6114589061146960405190565b639c86094560e01b8152918291600483016113a5565b9061043f91611374565b61044d906132a5565b61043f90336132ce565b61044d9061094f565b61044d600061149c565b61044d90610441565b61044d90546114af565b6114d26114ce8261338d565b1590565b6114e9576114e461044d916004610817565b6114b8565b611458906114f660405190565b637e27328960e01b81529182916004830190815260200190565b9061151a8161338d565b1561152d57611528916120aa565b600190565b61044d91613116565b61044d9392919061156b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a66133d0565b6133d0565b611650565b634e487b7160e01b600052601160045260246000fd5b9190611591565b9290565b820180921161159c57565b611570565b6001600160401b03811161094a5760208091020190565b906115c5610c44836115a1565b918252565b369037565b9061043f6115e56115df846115b8565b936115a1565b601f1901602084016115ca565b90600019905b9181191691161790565b9061161061044d6116179290565b82546115f2565b9055565b634e487b7160e01b600052603260045260246000fd5b9061163a825190565b81101561164b576020809102010190565b61161b565b50919261165d600061080a565b841461170f5761166b612382565b916116768584611586565b611689611685620208d561080a565b9190565b116116fd576116b5906116a58661169f816115cf565b966133db565b6116b0846017610817565b611602565b6116bf600061080a565b845b8110156116f6576116ef816116e96116dc6116c19487611586565b6116e68389611631565b52565b60010190565b90506116bf565b5092505090565b60405163318df65960e01b8152600490fd5b604051631758383360e11b8152600490fd5b61044d9291906060611536565b8181029291811591840414171561159c57565b61044d61174c6135a3565b7f0000000000000000000000000000000000000000000000120d4da7b0bd1400009061172e565b906115c5610c4483610c0c565b61044d6000611773565b61044d611780565b919061179d8261338d565b156117b6579061152892916117b061178a565b92612896565b61044d926121f2565b90610821565b61044d9081565b61044d90546117c5565b60016117ef61044d926117e7600090565b5060106117bf565b016117cc565b9061043f91611806611566826117d6565b90611810916135b6565b50565b9061043f916117f5565b7f00000000000000000000000000000000000000000000000000000000000000014603611868577ffee4e2a8816bc8a50511385138f948759f30bbeb14079b813bb7ff9de11b2f1990565b61044d61370d565b9061187a33610441565b6001600160a01b03821603611892576118109161379d565b60405163334bd91960e11b8152600490fd5b61044d905461085b565b61044d9081906001600160a01b031681565b805460009392916118d76118d38361085b565b9390565b916001811690811561192857506001146118f057505050565b6119039192939450600052602060002090565b6000905b8382106119145750500190565b600181602092548486015201910190611907565b60ff191683525050811515909102019150565b6105b16119539260209261194d815190565b94859290565b9384910161055a565b61197a611987936119746119749361044d97956118c0565b9061193b565b602f60f81b815260010190565b632e706e6760e01b815260040190565b6119ad6119a861044d836016610817565b6118a4565b6119ba611685600061080a565b11156119d3576119ce61044d916016610817565b61149c565b611a2c61044d61044d92611a12611a0c6119fc6119f76119f230610e87565b610e7e565b6118ae565b611a06601461080a565b9061385a565b916139be565b90611a1c60405190565b938492602084019260138461195c565b90810382520382610929565b909161043f926117b061178a565b6001600160a01b03909116815260408101929161043f9160200152565b611a706114ce8233613a51565b611a7d5761043f90613ab2565b61145890611a8a60405190565b63177e802f60e01b81529182913360048401611a46565b919081101561164b576020020190565b3561044d81610424565b91908092611ac8846115cf565b92611ad3600061080a565b855b811015611b0d57611b06816116e9611afc6112ba611af7611ad5968a8a611aa1565b611ab1565b6116e6838a611631565b9050611ad3565b509350505090565b91906105a781611b2c816105b19560209181520190565b8095610c28565b602080825261044d93910191611b15565b9160001960089290920291821b911b6115f8565b9190611b6961044d6116179361080a565b908354611b44565b61043f91600091611b58565b818110611b88575050565b80611b966000600193611b71565b01611b7d565b9190601f8111611bab57505050565b611bbd61043f93600052602060002090565b906020601f840181900483019310611bdf575b6020601f909101040190611b7d565b9091508190611bd0565b91906001600160401b03821161094a57611c0d82611c07855461085b565b85611b9c565b600090601f8311600114611c4857611617929160009183611c3d575b5050600019600883021c1916906002021790565b013590503880611c29565b90601f19831691611c5e85600052602060002090565b92825b818110611c9c57509160029391856001969410611c82575b50505002019055565b0135600019601f84166008021c19165b9055388080611c79565b92936020600181928786013581550195019301611c61565b9061043f9291611be9565b9493909192611cd46114ce838387878b613be2565b611cee575050611ce961043f93946016610817565b611cb4565b611458611cfa60405190565b631d9394a560e21b815292839260048401611b33565b61044d9061338d565b61043f90611d25613250565b611df9565b90611d33815190565b906001600160401b03821161094a57611d5082611c07855461085b565b602090601f8311600114611d8a57611617929160009183611d7f575050600019600883021c1916906002021790565b015190503880611c29565b601f19831691611d9f85600052602060002090565b9260005b818110611dd757509160029391856001969410611dc35750505002019055565b01516000196008601f8516021c1916611c92565b91936020600181928787015181550195019201611da3565b9061043f91611d2a565b61043f906014611def565b61043f90611d19565b61043f90611e19613250565b61043f906013611def565b61043f90611e0d565b61044d61044d61044d926001600160801b031690565b916001600160a01b0360089290920291821b911b6115f8565b9190611e6d61044d61161793610e87565b908354611e43565b61043f91600091611e5c565b805482101561164b57611e9b600191600052602060002090565b91020190600090565b90815491600160401b83101561094a5782611ec791600161043f95018155611e81565b90611b58565b9061161061044d6116179261080a565b90600d611ef2611eec83611e2d565b82613d41565b90611eff6114ce8561338d565b61206d57611f106114ce8533613a51565b61206057611fdd611fce611fca61200893611f76611fa089611f3181613d9e565b94829c929691611f4c6000611f47876004610817565b611e75565b60089685611f6c611f6684611f618d8d610e90565b611e81565b90610ea5565b60a01c928a613dcc565b611f8b6000611f86856009610817565b611b71565b611f9b83600a613e2d565b613e2d565b613e9e565b611fc56001600160a01b038816600160a01b01611fc061044d8585610e90565b611ea4565b610e90565b5490565b611fd8600161080a565b900390565b6001600160a01b03841660a09190911b6001600160a01b03191601612003836009610817565b611ecd565b6001600160a01b037f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d81169216908183600080516020615b6b833981519152600080a4600080516020615b6b833981519152600080a4565b61145884611a8a60405190565b611458846114f660405190565b61044d600d613f05565b906001600160a01b03906115f8565b906120a361044d61161792610e87565b8254612084565b6120b38261213e565b906001600160a01b0382163314158061212b575b612113576120df816120da856004610817565b612093565b6001600160a01b0390811691167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600080a4565b60405163a9fbf51f60e01b8152336004820152602490fd5b506121396114ce3384612e68565b6120c7565b61214a6114ce8261338d565b6114e95761216c61216561217192612160600a90565b614043565b6009610817565b6117cc565b6001600160a01b0316906121886104416000611393565b6001600160a01b0383161461219957565b7f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d9150565b6001600160a01b03909116815260608101939261043f9290916040916113c8906020830152565b9190820391821161159c57565b9291906122026104416000611393565b6001600160a01b038216148015612303575b80156122e8575b6122b757336001600160a01b0385160361223a575b61044d9293614150565b61225261216c61224b866006610e90565b3390610e90565b936000198503612264575b9350612230565b8483116122925761228a61227c8461044d96976121e5565b61200361224b846006610e90565b84935061225d565b8490611458846122a160405190565b637dc7a0d960e11b8152938493600485016121be565b611458906122c460405190565b63ec442f0560e01b8152918291600483016001600160a01b03909116815260200190565b506001600160a01b0384166001600160a01b0382161461221b565b506001600160a01b037f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d166001600160a01b03821614612214565b61216c61044d9161234d600090565b506002610e90565b61235d613250565b61043f61043f61236d6000611393565b6142fe565b61043f612355565b61044d61434a565b61044d60036117cc565b61044d6135a3565b9061043f916123a1613250565b9190823b6123b2611685600061080a565b146123c15761043f91926132ce565b611458836123ce60405190565b639c86094560e01b8152918291600483016001600160a01b03909116815260200190565b9061043f91612394565b61044d60116114b8565b61044d600d614354565b61044d905b60ff1690565b61044d9054612410565b61044d91600061243a612440936117e7600090565b01610e90565b61241b565b61044d600161149c565b9061043f929161245d613250565b6124a7565b9050519061043f82610450565b9060208282031261042d5761044d91612462565b6001600160a01b0391821681529116602082015260608101929161043f9160400152565b6124b090610e87565b906124be6104416000611393565b6001600160a01b038416146123c1576124df6124d930610e87565b92610e87565b6040516331a9108f60e11b815260048101839052602081602481855afa80156114245761251b916000916125a9575b506001600160a01b031690565b6001600160a01b0384160361258557803b1561042d5761255c936000809461254260405190565b96879586948593632142170760e11b855260048501612483565b03925af180156114245761256d5750565b61043f90600061257d8183610929565b81019061054f565b509061145861259360405190565b633f82dd3d60e11b815292839260048401611a46565b6125cb915060203d6020116125d1575b6125c38183610929565b81019061246f565b3861250e565b503d6125b9565b9061043f929161244f565b9060ff906115f8565b906125fc61044d61161792151590565b82546125e3565b90336001600160a01b038316146126685761262c8161262784611fc5336005610e90565b6125ec565b60009081526001600160a01b03919091169033907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3190602090a3565b6114588261267560405190565b630b61174360e31b8152918291600483016001600160a01b03909116815260200190565b6126a4919033611792565b50600190565b61244061044d916126b9600090565b506007610e90565b61044d601461149c565b906126e66126da6106ad845490565b92600052602060002090565b9060005b8181106126f75750505090565b90919261271b61271460019261270c876117cc565b815260200190565b9460010190565b9291016126ea565b9061044d916126cb565b9061043f6109699261273e60405190565b93848092612723565b61044d9061272d565b906001600160a01b037f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d166001600160a01b0383161461288c5760006127958161080a565b926127ab6127a66000926008610e90565b612747565b91845b6127b961044d855190565b8610156127e0576001906127d46127d08887611631565b5190565b60a01c019501946127ae565b6127ee9193949295506115cf565b9182946127fa8261080a565b905080915b61280a61044d875190565b8310156128845761281e6127d08488611631565b60a081901c966001600160a01b03909116949061283a8361080a565b935b8885101561286757612714612861916116e9612857888b0190565b6116e6838d611631565b9361283c565b9893509390945061287a91965060010190565b91949590956127ff565b505050915050565b905061044d61207a565b939291906128a66114ce8361338d565b6128d2576128b76114ce8333613a51565b6128c55761043f93946143d2565b61145882611a8a60405190565b611458826114f660405190565b61043f906128eb613250565b61043f906144d6565b61043f906128df565b9092919261290d610c4482610c0c565b938185528183011161042d5761043f91602085019061055a565b9080601f8301121561042d57815161044d926020016128fd565b9060208282031261042d5781516001600160401b03811161042d5761044d9201612927565b60006129ae91612974606090565b5061298d61298861298861298860156114b8565b610e87565b6040519384928391829163c87b56dd60e01b83526004830190815260200190565b03915afa908115611424576000916129c4575090565b61044d91503d806000833e6129d98183610929565b810190612941565b949290979695939160e086019860008701612a03916001600160a01b03169052565b6001600160a01b031660208601526040850152606084015260ff16608083015260a082015260c00152565b9194612a7d6113c892989795612a7660a096612a6661043f9a612a5660c08a019e60008b0152565b6001600160a01b03166020890152565b6001600160a01b03166040870152565b6060850152565b6080830152565b6020809392612aa16115c56105b19461190160f01b815260020190565b01918252565b6113c861043f94612ad0606094989795612ac6608086019a6000870152565b60ff166020850152565b6040830152565b9593919897969492909861010087019960008801612afc916001600160a01b03169052565b6001600160a01b031660208701526001600160a01b031660408601526060850152608084015260ff1660a083015260c082015260e00152565b95909394929192612b434290565b8310612d9557612b528461338d565b612d6e57600095612b6287611393565b936001600160a01b0385166001600160a01b038816148015612d33575b612d025760208883611a2c612c1e8d8b612bf48e612b9b61181d565b93611a2c8b612bab83600f610e90565b612bc3612bb7826117cc565b91600183015b90611ecd565b6040519687958e8701957f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c987612a2e565b612c06612bff825190565b9160200190565b2090612c1160405190565b9384928884019283612a84565b612c29612bff825190565b20612c418888612c3860405190565b94859485612aa7565b838052039060015afa15611424578751946001600160a01b03166001600160a01b038616148015612ce6575b612cc057505050509181612cad60209361200384611fc57f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925986006610e90565b84526001600160a01b03908116941692a3565b61145894958997612cd060405190565b63d01a799560e01b815298899860048a01612ad7565b506001600160a01b0389166001600160a01b0386161415612c6d565b61145887612d0f60405190565b634a1406b160e11b8152918291600483016001600160a01b03909116815260200190565b506001600160a01b037f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d166001600160a01b03881614612b7f565b61145884612d7b60405190565b635dbad48560e01b81529182916004830190815260200190565b85611458938895612da560405190565b63dc99de6360e01b8152978897600489016129e1565b9061043f91612dcc611566826117d6565b906118109161379d565b9061043f91612dbb565b61044d91611fc561216c92612df3600090565b506006610e90565b600019811461159c5760010190565b90600090612e18600061080a565b612e22600061080a565b905b612e2d8461080a565b8103612e605750612e2d612e58612e5261216c6017612e4c868a6121e5565b90610817565b92612dfb565b919050612e24565b925050915090565b61044d91611fc561244092612e7b600090565b506005610e90565b9061043f9291612e91613250565b612ef3565b9050519061043f82610424565b9060208282031261042d5761044d91612e96565b9050519061043f8261050e565b9060208282031261042d5761044d91612eb7565b91602061043f9294936113c86040820196600083019061139c565b612eff90929192610e87565b612f0c6104416000611393565b612f1583610e87565b1461308b57612f2390610e87565b906370a08231612f3230610e87565b90612f3c60405190565b612f468260e01b90565b81526001600160a01b0383166004820152602081602481885afa801561142457612f7591600091613072575090565b8511612fe857505091602091612faf936000612f9060405190565b809681958294612fa463a9059cbb60e01b90565b845260048401612ed8565b03925af1801561142457612fc05750565b6118109060203d602011612fe1575b612fd98183610929565b810190612ec4565b503d612fcf565b6020613021928561300a8894612ffd60405190565b9687948593849360e01b90565b83526001600160a01b031660048301526024820190565b03915afa91821561142457600092613041575b5061145861144260405190565b61306491925060203d60201161306b575b61305c8183610929565b810190612ea3565b9082613034565b503d613052565b61044d915060203d60201161306b5761305c8183610929565b6114588261146960405190565b9061043f9291612e83565b61043f906130af613250565b6130b96000611393565b6001600160a01b0381166001600160a01b038316146130dc575061043f906142fe565b611458906130e960405190565b631e4fbdf760e01b8152918291600483016001600160a01b03909116815260200190565b61043f906130a3565b906131246104416000611393565b6001600160a01b038316148015613199575b61318c5761314d8161200384611fc5336006610e90565b60009081526001600160a01b03919091169033907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3600190565b61145882612d0f60405190565b506001600160a01b037f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d166001600160a01b03831614613136565b61043f906132017fdb2819cfe36df1a8e2df5d8d9cfe49bf1b7c55ce41ab4723ae144d927ae5245b6133d0565b61043f906015612093565b61043f906131d4565b7f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f8852587461323f60405190565b8061324b343383611a46565b0390a1565b6132586123fc565b339061326c825b916001600160a01b031690565b036132745750565b6114589061328160405190565b63118cdaa760e01b8152918291600483016001600160a01b03909116815260200190565b637965db0b60e01b6001600160e01b03198216149081156132c4575090565b61044d91506144e1565b91906132dd6104416000611393565b6001600160a01b038416148015613352575b6133215761262761043f9293826000146133135761330c81614640565b6007610e90565b61331c816144fb565b61330c565b6114588361332e60405190565b63024b6d7560e31b8152918291600483016001600160a01b03909116815260200190565b506001600160a01b037f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d166001600160a01b038416146132ef565b61339881600b614752565b6133ca576133a961044d60036117cc565b811090816133b5575090565b90506133c561168561158d614794565b111590565b50600090565b61043f90339061479e565b6000906133e78261080a565b8314613591576133f961044183611393565b6001600160a01b038216148015613556575b6122b75761341960036117cc565b917f0000000000000000000000000000000000000000000000120d4da7b0bd140000840261345861344b846002610e90565b612bbd836105b1836117cc565b8152838301906001600160a01b038316908181600080516020615b6b833981519152602082a361348c612440856007610e90565b156134bf5750505050816134ae61043f936134b8936134a9600a90565b6148af565b6105b160036117cc565b6003611ecd565b90919293806134d2600192611f96600a90565b6134dd8787836147d6565b61350a6134ee611fca886008610e90565b9661350384986134fd8661080a565b906121e5565b908361481f565b808484600080516020615b6b8339815191528180a4015b8381036135395750505050506134b861043f916134ae565b8085918484600080516020615b6b8339815191528180a401613521565b506001600160a01b037f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d166001600160a01b0382161461340b565b604051636e45634760e11b8152600490fd5b61044d6135ae6149b4565b6134fd61434a565b6135c36114ce8383612425565b1561362c576135de600161262784600061243a8660106117bf565b6135f86135f26135ec339390565b93610e87565b91610e87565b917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d61362360405190565b600090a4600190565b5050600090565b805460009392916136466108988361085b565b91600181169081156108f4575060011461365f57505050565b6136729192939450600052602060002090565b916000925b8184106136845750500190565b805484840152602090930192600101613677565b9061044d91613633565b9061043f610969926136b360405190565b93848092613698565b61044d906136a2565b9095949261043f946136f76136fe926136f06080966136e960a088019c6000890152565b6020870152565b6040850152565b6060830152565b01906001600160a01b03169052565b611a2c61378e61371d60006136bc565b613728612bff825190565b2061373230610e87565b9061373c60405190565b93849260208401927fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc646917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f866136c5565b613799612bff825190565b2090565b6137a78282612425565b1561362c576137c16000612627848261243a8660106117bf565b6137cf6135f26135ec339390565b917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b61362360405190565b9061043f6115e561380a84611773565b93610c0c565b90613819825190565b81101561164b570160200190565b801561159c576000190190565b61241561044d61044d9290565b61044d9061385561168561044d9460ff1690565b901c90565b908161388a61388561387584613870600261080a565b61172e565b61387f600261080a565b90611586565b6137fa565b92600060306138a161389b8361080a565b87613810565b536001600f60fb1b821a6138bd6138b78361080a565b88613810565b53806138d96138d087613870600261080a565b61387f8361080a565b915b61391a575b50506116856138ee9161080a565b036138f857505090565b61145861390460405190565b63e22e27eb60e01b8152928392600484016113b5565b90926139258261080a565b841115613993576f181899199a1a9b1b9c1cb0b131b232b360811b61394a600f61080a565b821690601082101561164b57839261396a6139879261398d941a60f81b90565b861a613976888c613810565b536139816004613834565b90613841565b94613827565b916138db565b926138e0565b634e487b7160e01b600052601260045260246000fd5b81156139b9570490565b613999565b6139c7816149c9565b906139d86001926105b1600161080a565b91806139e3846137fa565b936020018401905b6139f6575b50505090565b8115613a4c57613a309060001901926f181899199a1a9b1b9c1cb0b131b232b360811b600a82061a8453613a2a600a61080a565b906139af565b9081613a3f611685600061080a565b14613a4c579091816139eb565b6139f0565b613a5a8261213e565b6001600160a01b0381166001600160a01b03831614928315613a8d575b508215613a8357505090565b61044d9250612e68565b613a989193506114c2565b613aaa6001600160a01b03831661325f565b149138613a77565b613abe6114ce8261338d565b6114e9576000613aff613ad083613d9e565b613ae285611f47886004979697610817565b85613af5611f6684611f61886008610e90565b60a01c9285613dcc565b613b0e82611f86856009610817565b613b1983600a613e2d565b613b2483600b613e2d565b7f0000000000000000000000000000000000000000000000120d4da7b0bd14000090613b65613b54826002610e90565b612bbd84613b61836117cc565b0390565b613b7c613b756116e9600c6117cc565b600c611ecd565b9082526001600160a01b03168181600080516020615b6b833981519152602083a3600080516020615b6b8339815191528280a4565b61044d913691610c34565b6136fe61043f94612ad0606094989795613bdb608086019a6000870152565b6020850152565b9390613bee60126114b8565b613bfe61325f6104416000611393565b14613ce757613cbb613ccd93611a2c92613c97613cd398613c6e613c687f0e7a552dfd1dbc578a80e6130b42ccb1e661a00a922ebfd0a0def6c5f5501fe495613c647f114ba586f27424819fdf1eac564faf22081abcab4f662a9e508c112ed1a7cbcf90565b9591565b90613bb1565b613c79612bff825190565b2090611a2c613c8760405190565b9485936020850193339285613bbc565b613ca2612bff825190565b2090613cad60405190565b938492602084019283612a84565b613cc6612bff825190565b2092613bb1565b90614b6b565b613ce361325f61044160126114b8565b1490565b6040516395e0712b60e01b8152600490fd5b61044d905b6001600160801b031690565b61044d9054613cf9565b613cfe61044d61044d9290565b613cfe61044d61044d926001600160801b031690565b9061082190613d21565b90613d4e61044d83614354565b811015613d8c5781613d8661216c92613d79613d736000600161044d98019501613d0a565b91613d14565b016001600160801b031690565b90613d37565b60405163580821e760e01b8152600490fd5b613da990600a614043565b613db761216c826009610817565b6001600160a01b0381169260a09190911c9190565b93919092613dd78290565b8103613e045750613de8600161080a565b8203613df957505061043f91614d2b565b909161043f93614c78565b919261043f94614b81565b61044d600160ff1b61080a565b61044d9061385561168561044d9490565b613e6e61043f926000613e67613e4c613e466008613834565b84613841565b92613e55613e0f565b90613e6060ff61080a565b1690613e1c565b9301610817565b90613e78826117cc565b1790611ecd565b6001600160801b03908116911601906001600160801b03821161159c57565b9190613eac61044d84614354565b613eb582611e2d565b1015613d8c5782613d8661200392613ed66000600161043f98019401613d0a565b90613e7f565b61044d9060801c613cfe565b61044d9054613edc565b600161044d91016001600160801b031690565b613f1d613f2a82613f1581613d0a565b928391613ee8565b036001600160801b031690565b91613f3c613f3784611e2d565b6115cf565b938490613f496000613d14565b6001600160801b0386165b6001600160801b0382161015613fa857613fa1613f5491613f9c613f8a61216c60018901613d86858c016001600160801b031690565b6116e6613f9684611e2d565b8c611631565b613ef2565b9050613f49565b50935093505050565b15613fb857565b60405162461bcd60e51b815260206004820152603460248201527f4269744d6170733a205468652073657420626974206265666f7265207468652060448201527334b73232bc103237b2b9b713ba1032bc34b9ba1760611b6064820152608490fd5b61044d9061402e61168561044d9460ff1690565b901b90565b61044d61044d61044d9260ff1690565b6000916140506008613834565b9061405b8282613841565b92839161408e61407c61216c60ff9361407460ff61080a565b169785610817565b8661408760ff61080a565b1890613e1c565b9081966000926140a06116858561080a565b11156140cd57505050506140c46140be6140c99493611fd89361401a565b94614f3e565b614033565b1790565b92979650929094505b6140f4906140ed6140e68761080a565b8211613fb1565b6000190190565b908161410561216c82888b01610817565b9061410f8761080a565b821161411d575050906140d6565b61413d919798506140c9965061414993506141439250946140c49561401a565b95614f3e565b91613834565b0360ff1690565b9160029261416161216c8286610e90565b61416e61216c8587610e90565b928082106142da576141906141838488610e90565b612bbd83613b61836117cc565b61419d61344b8688610e90565b60008181526001600160a01b038681169190851690600080516020615b6b83398151915290602090a36141d4612440846007610e90565b6141e2612440876007610e90565b9080806142d3575b156141fd575b5050505050505050600190565b15614261575050505061424e90611fd861421d61216c8561425497610e90565b6142487f0000000000000000000000000000000000000000000000120d4da7b0bd14000080926139af565b926139af565b90615361565b38808080808080806141f0565b949092949391936000146142c5575050506142c092611fd86142ba926142b561216c866142af7f0000000000000000000000000000000000000000000000120d4da7b0bd14000080956139af565b95610e90565b6139af565b906151e4565b614254565b909291936142c09550614f97565b50816141ea565b61145883916142e860405190565b63391434e360e21b8152938493600485016121be565b6143196135f261430e60116114b8565b612988846011612093565b907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e061434460405190565b600090a3565b61044d600c6117cc565b614387600061044d92614365600090565b500161437961437382613ee8565b91613d0a565b90036001600160801b031690565b611e2d565b6104dd9061080a565b6001600160a01b03909116815261044d93608082019390926143c591906143bb90613bdb565b604083019061438c565b606081840391015261057d565b9093926143ee6114ce826143e6600161080a565b86898761541b565b6144af57506144006104416000611393565b6001600160a01b038516148015614474575b8015614459575b614428579261043f9293615585565b6114588461443560405190565b633250574960e11b8152918291600483016001600160a01b03909116815260200190565b506001600160a01b0381166001600160a01b03851614614419565b506001600160a01b037f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d166001600160a01b03851614614412565b84611458849260016144c060405190565b633f756b1760e01b815294859460048601614395565b61043f906012612093565b613ce36301ffc9a760e01b5b916001600160e01b03191690565b61453361450c61216c836002610e90565b7f0000000000000000000000000000000000000000000000120d4da7b0bd140000906139af565b9061453e600061080a565b9061454d6127a6826008610e90565b93825b61455b61044d875190565b84101561457e576001906145726127d08689611631565b60a01c01930192614550565b9194509203905090614590600061080a565b825b8110156145b0576145a9614592916116e9866156fa565b9050614590565b50915050565b8181106145c1575050565b806145cf6000600193611b71565b016145b6565b9190918282106145e457505050565b61043f92916145f99091600052602060002090565b91820191016145b6565b90600160401b811161094a578161461b61043f935490565b908281556145d5565b600061043f91614603565b906000036109815761043f90614624565b9061464f6127a6836008610e90565b9060009061465c8261080a565b905b61466961044d855190565b82101561473b57906146a29161467f8287615778565b61469186611f86846009989598610817565b61469d8186600a6148af565b840190565b947f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d935b8681101561471d57806146e187611f47614718946004610817565b6146f181600d6157cd565b6157cd565b806001600160a01b03878116908b16600080516020615b6b8339815191528a80a460010190565b6146c6565b509450915061044d6147326146699260010190565b9291505061465e565b9361043f935061474d91506008610e90565b61462f565b9061477f61216c61478f92614765600090565b506000614778613e4c613e466008613834565b9501610817565b9161478a600061080a565b921690565b141590565b61044d600161080a565b906147ac6114ce8284612425565b6147b4575050565b6114586147c060405190565b63e2517d3f60e01b815292839260048401611a46565b61044d611fc09161043f946147e9600090565b506001600160a01b0390911660a09190911b6001600160a01b03191601926008610e90565b61044d6001600160a01b031961080a565b9092919261483b61044d61483161480e565b61398160a0613834565b8411614877576120039061043f9394614852600090565b506001600160a01b0390911660a09190911b6001600160a01b03191601916009610817565b6114588461488460405190565b631605cc3f60e11b81529182916004830190815260200190565b61044d9061402e61168561044d9490565b916148c36148bd6008613834565b83613841565b80926148cf60ff61080a565b1691610100918184016148e46116858561080a565b101561491e5750613e67613e6e9361491961043f979694614913600095613b6161490d60001990565b9361080a565b9061489e565b613e1c565b959491935061495e9061495790611fd86000199561494e6000614941838a613e1c565b970196613e6e8c89610817565b613b618861080a565b9560010190565b945b6149698461080a565b8111156149945761495761498e91614985856120038a87610817565b611fd88661080a565b94614960565b613e6e926149136149ae92613b6161043f9899959761080a565b92610817565b61044d6149c160036117cc565b6134fd614794565b6149d3600061080a565b907a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000006149f98161080a565b821015614b49575b506d04ee2d6d415b85acef8100000000614a1a8161080a565b821015614b27575b50662386f26fc10000614a348161080a565b821015614b05575b506305f5e100614a4b8161080a565b821015614ae3575b50612710614a608161080a565b821015614ac1575b50614a73606461080a565b811015614a9f575b614a88611685600a61080a565b1015614a915790565b61044d906105b1600161080a565b614ab0614abb91613a2a606461080a565b916105b1600261080a565b90614a7b565b614adc91613a2a614ad19261080a565b916105b1600461080a565b9038614a68565b614afe91613a2a614af39261080a565b916105b1600861080a565b9038614a53565b614b2091613a2a614b159261080a565b916105b1601061080a565b9038614a3c565b614b4291613a2a614b379261080a565b916105b1602061080a565b9038614a22565b614b6491613a2a614b599261080a565b916105b1604061080a565b9038614a01565b61044d91614b789161582b565b909291926158cb565b909392600a93614b918486613e2d565b614bc5828503926008976001600160a01b0390911660a085901b6001600160a01b0319160190611ec790611f61878b610e90565b614bd4600191611fd88361080a565b95868310614be6575b50505050505050565b614c42611fca85611fd893611fc5614c1a614c4898614c6c9d613b61614c126120039e6105b18d61080a565b9e8f90613e2d565b6001600160a01b038c1660a09190911b6001600160a01b03191601611fc061044d8585610e90565b9161080a565b6001600160a01b0390911660a09190911b6001600160a01b03191601916009610817565b38808080808080614bdd565b9190611f6161043f94614c92611ec7946105b1600161080a565b90614c9e82600a613e2d565b614cc66001600160a01b03871660a086901b6001600160a01b03191601612003846009610817565b6001600160a01b0390911660001990910160a01b6001600160a01b03191601936008610e90565b634e487b7160e01b600052603160045260246000fd5b80548015614d26576000190190614d23614d1d8383611e81565b90611b71565b55565b614ced565b9061043f91614d3e61044d826008610e90565b91614d4a611fce845490565b818103614d5a575b505050614d03565b614da492614d6e611f666120039387611e81565b614d7c81611ec78689611e81565b6001600160a01b0391821660a09490941b6001600160a01b0319169390930192166009610817565b388080614d52565b614db7610100611773565b7e01020903110a19042112290b311a3905412245134d2a550c5d32651b6d3a7560208201527f06264262237d468514804e8d2b95569d0d495ea533a966b11c886eb93bc176c960408201527f071727374353637324837e9b47af86c7155181ad4fd18ed32c9096db57d59ee360608201527f0e2e4a6a5f92a6be3498aae067ddb2eb1d5989b56fd7baf33ca0c2ee77e5caf760808201527fff0810182028303840444c545c646c7425617c847f8c949c48a4a8b087b8c0c860a08201527f16365272829aaec650acd0d28fdad4e22d6991bd97dfdcea58b4d6f29fede4f660c08201527ffe0f1f2f3f4b5b6b607b8b93a3a7b7bf357199c5abcfd9e168bcdee9b3f1ecf560e08201527ffd1e3e5a7a8aa2b670c4ced8bbe8f0f4fc3d79a1c3cde7effb78cce6facbf9f861010082015290565b61044d614dac565b61044d614eea565b61044d7e818283848586878898a8b8c8d8e8f929395969799a9b9d9e9faaeb6bedeeff61080a565b61241561044d61044d9260ff1690565b61044d9060f81c614f22565b614f92614f8461044d92614f50600090565b50614f7e614f74614f68614f62614ef2565b9361599b565b614f70614efa565b0290565b61398160f8613834565b90613810565b516001600160f81b03191690565b614f32565b909194614fa2600090565b50614fce7f0000000000000000000000000000000000000000000000120d4da7b0bd14000080966139af565b93600095614fdc600061080a565b86811015615173579786979860089795969790614ffc611fca8984610e90565b918961500d6001946134fd8661080a565b918a6150198482615778565b94909b61502c61168561158d8989611586565b1161510b57615083611fca8585611fc561507e61044d615093986150788f9d9b99615072611f6661509b9f61044d6150679161508c9f610e90565b92611f61868b610e90565b90611ea4565b84610e90565b614d03565b6134fd8661080a565b8d8c61481f565b898101930190565b9789918d915b905b6150b7575b50505050509796959493614fdc565b8b8482101561510557859383600080516020615b6b83398151915284936150e66150fc97611f47876004610817565b6001600160a01b0390811693169180a460010190565b8c908a926150a3565b506150a8565b9250509261511b908d9a939a0390565b98899003908161512c918c856159c1565b019061513982600a613e2d565b896151458982856147d6565b61514e91610e90565b546151588361080a565b61516491038a8361481f565b958601818a9789918d916150a1565b506151bf94965080611685946151a5615193611fd89461158d979c6139af565b611fd8846142b561216c866002610e90565b88106151d0575b50614248816142b561216c8b6002610e90565b116151c75750565b61043f906156fa565b6151de906142ba600161080a565b386151ac565b91906000926151f3600061080a565b925b8284101561535a5760086152098382610e90565b90615233611f6661521d611fca8785610e90565b9361522d600195611fd88761080a565b90611e81565b60018060a01b0381169060a01c87868a61524d84809c0190565b8a106153145761507e61044d61528295949387986152766105b195611f866152889b6009610817565b611fc5878b600a6148af565b96820190565b7f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d91835b6152ba575b505050506151f5565b8181101561530f57615309816152d386936146ec600d90565b6152e28b611f47836004610817565b806001600160a01b03868116908a16600080516020615b6b8339815191528e80a460010190565b906152ac565b6152b1565b50508703905096879003858282019381858b81019b61533491600a6148af565b61533d91610e90565b546153478661080a565b900390615353936159c1565b8495615288565b9350505050565b9161536c600061080a565b825b8110156145b05761538561536e916116e9866156fa565b905061536c565b9050519061043f826104ac565b9060208282031261042d5761044d9161538c565b6001600160a01b0391821681529116602082015261044d9260808201929091906143c590612ad0565b3d156153f0576153e53d611773565b903d6000602084013e565b606090565b6001600160a01b03909116815261044d93608082019390926143c59190612ad090613bdb565b93919291600092823b6154306116858661080a565b111561557a576001936001968587905b615450575b505050505050505090565b61545d61044d858a611586565b8110156155755761547061298887610e87565b602061547b60405190565b918290630a85bd0160e11b825281878161549b8c898b33600486016153ad565b03925af160009181615545575b506155155750866154c1578691906001015b9091615440565b50508594506154ce6153d6565b906154e36116856154dd845190565b9261080a565b0361550d575090611458916154f760405190565b633f756b1760e01b8152948594600486016153f5565b805190602001fd5b896154ba918994939b9161552c575b509960010190565b905061553e630a85bd0160e11b6144ed565b1438615524565b61556791925060203d811161556e575b61555f8183610929565b810190615399565b90386154a8565b503d615555565b615445565b505050505050600190565b91816155c461559383613d9e565b6155a66000611f47886004979697610817565b856155ba611f6684611f6160089889610e90565b60a01c9289613dcc565b6155d2612440836007610e90565b1561569a5750506155e581611f96600a90565b6155f081600d6157cd565b6001600160a01b037f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d8116908416600080516020615b6b833981519152600080a45b7f0000000000000000000000000000000000000000000000120d4da7b0bd140000615661614183846002610e90565b61566f61344b836002610e90565b60009081526001600160a01b039182169290911690600080516020615b6b83398151915290602090a3565b6156d491611fdd916156ca91611fca91611fc56001600160a01b038816600160a01b01611fc061044d8585610e90565b6134fd600161080a565b6001600160a01b03828116908416600080516020615b6b833981519152600080a4615632565b615704600d615a41565b9061573561572e615715600161080a565b6157208185876147d6565b613b61611fca856008610e90565b828461481f565b6001600160a01b03908116907f000000000000000000000000e25df5e33f915ab698e12e62fa2d8e3cacbb859d16600080516020615b6b833981519152600080a4565b61579391611f61611f669261578b600090565b506008610e90565b6001600160a01b0381169160a09190911c90565b906001600160801b03906115f8565b906157c661044d61161792613d21565b82546157a7565b806157e46157da82613d0a565b6143796001613d14565b916157f1613cfe83613ee8565b6001600160801b038416146158195761043f936120038460016158149401613d37565b6157b6565b604051638acb5f2760e01b8152600490fd5b90600091615837825190565b615844611685604161080a565b0361586e5761586792506020820151906060604084015193015160001a90615aac565b9192909190565b50905061588b6158866158816000611393565b925190565b61080a565b909160029190565b634e487b7160e01b600052602160045260246000fd5b600411156158b357565b615893565b9061043f826158a9565b61044d9061080a565b6158d560006158b8565b6158de826158b8565b036158e7575050565b6158f160016158b8565b6158fa826158b8565b036159115760405163f645eedf60e01b8152600490fd5b61591b60026158b8565b615924826158b8565b0361595257611458615935836158c2565b60405163fce698f760e01b81529182916004830190815260200190565b61596561595f60036158b8565b916158b8565b1461596d5750565b6114589061597a60405190565b6335e2f38360e21b81529182916004830190815260200190565b1561042d57565b6159af6159a8600061080a565b8211615994565b6159bd81613b61600061080a565b1690565b91611f61611ec7929361043f956159d6600090565b506001600160a01b0390911660a09190911b6001600160a01b03191601936008610e90565b600161044d91036001600160801b031690565b906fffffffffffffffffffffffffffffffff199060801b6115f8565b90615a3a61044d61161792613d21565b8254615a0e565b9081615a4c81613ee8565b90615a59613cfe85613d0a565b6001600160801b03831614615a9a576001615a7661043f936159fb565b910193615a956000611f8684615a8f61216c828b613d37565b98613d37565b615a2a565b6040516375e52f4f60e01b8152600490fd5b9091615ab7846158c2565b615ae36116857f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a061080a565b11615b565790615afc60209460009493612c3860405190565b838052039060015afa1561142457600051600091615b1983611393565b6001600160a01b0381166001600160a01b03841614615b425750615b3c8361080a565b91929190565b915091615b4e9061080a565b909160019190565b505050615b636000611393565b916003919056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212202701fb4ac6bfe60b7631cc1bea381b78d9c385162634a0e7a45801e33efeef5464736f6c63430008190033

Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.