Contract 0xb66bf78cad7cbab51988ddc792652cbabdff7675 9

 
 
Txn Hash
Method
Block
From
To
Value [Txn Fee]
0x8a0abbef8acc2ebcb6ce8d156bece2d52ef22b2e9b92500765ee77a097dc00adUse Voucher(pending)2023-05-28 20:39:296 secs ago0x766873db8e2a82cbda14a40ab7a7649cfe8cffff IN 0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC(Pending)
0x75b243b6b7762024b5da0d0ae3d5895825351590e143fdc140e94121e3ba97bdUse Voucher(pending)2023-05-28 20:39:296 secs ago0x766873db8e2a82cbda14a40ab7a7649cfe8cffff IN 0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC(Pending)
0x19a00dbec08020b5049a77a7f0915faefb889d0565afcf31b7a9d748692399ceUse Voucher432624422023-05-28 20:38:4649 secs ago0x87c01d6bd57e45c6b7f89a72ce1d78a7e94ff3d0 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.34571362008145.666117121
0x5e34b8f1e99a81ab31eb70c060540da48fa0012cd72f46dafb0385c94dfcc475Use Voucher432624122023-05-28 20:37:421 min ago antonian-ernest.wallet  IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.03228096269 144.424144644
0xebb1841701175b373d6c1ae1dd8f542f27608fdabd5f75db8e8c84350f9c7244Use Voucher432624032023-05-28 20:37:242 mins ago nthg0069.wallet  IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.032630803415 146.159339838
0x4411e697c597682f7a2c57b9f83c8ad2bd6b667957b05f57b11a236e43c4ad1aUse Voucher432623702023-05-28 20:36:143 mins ago0x776171969075691310d17943b150457796465f43 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.060080988256 160.038006421
0x0ad1a25a636031ff7fcbe9d8c283915294cb6742f8e42e8f8329cc2a5c457eb2Use Voucher432623532023-05-28 20:35:383 mins ago0xcf0e44b96ad7e6151c0a83107068a8bd071dad08 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.057084992727 152.679800922
0x9f6d7df9419862b6c472c86ef1d5d7e0e4ecd98c7b0954c9dfc2f61352a8c27dUse Voucher432623402023-05-28 20:35:104 mins ago addressbnb11.wallet  IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.054763142334146.279235029
0x3d6f7b4037246a1cac533edb5bc90da62323631d35c975a43fd00988a22e0eddUse Voucher432623322023-05-28 20:34:524 mins ago0x7e1bd296805ef0137874e43a34d8eda28303e858 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.036332914604162.762915629
0x5007c6e4345fab892e4837b574db82e0a374046c04c4c94748b18fa6a11e79a8Use Voucher432622952023-05-28 20:33:346 mins ago0xe3c9f9291c8339d42489e90b035dabc549279830 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.357666650927151.768327799
0x3d52114a0d249c24b059d5732c35cb15a0f77658a08698938cd7fd8e8bc731ffUse Voucher432622892023-05-28 20:33:226 mins ago0x776171969075691310d17943b150457796465f43 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.02904963775 130.051070868
0x4fd6cecd45c5f37007742798bbb11cb19d3ffdbabdd6ae3a3a13295e330a35fbMeld Flock432622862023-05-28 20:33:136 mins ago0xbe0bcd648a784545cb379d201de6217e1fa638c2 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.041932146279 137.059584298
0x35a2d6dc7085fee2c11fed4084ad39f4bc58f8501cf13369ab64b42c9d17517aUse Voucher432622772023-05-28 20:32:556 mins ago0x776171969075691310d17943b150457796465f43 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.32892529174 138.572868912
0xc5499717295907fddd22d9522840447a3373f5506e8ba09f1f99ff7cb31e7678Use Voucher432622642023-05-28 20:32:277 mins ago0xe3c9f9291c8339d42489e90b035dabc549279830 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.038012784051158.197422464
0x37c051816d4745cdc2d62ebf871b7b70f9e7868e250a7ac0b82997639f810c54Use Voucher432622472023-05-28 20:31:517 mins ago addressbnb11.wallet  IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.050064023298136.160832289
0xa72120540b0d0cb412aad13efa463198a0d65c0706228a57b9f9bc799fa18e4cMint Pack432622402023-05-28 20:31:377 mins ago zelthasoftware.zil  IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.03151873494 149.325520625
0xe3a6fd3b027a6748a052c1a05f071a0c168ccd7f442d655e64053f09e3bb18eaMint Pack432622282023-05-28 20:31:118 mins ago zelthasoftware.zil  IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.035069618303 159.430545824
0xb1b3d5d70e4d6a87995a67182ca6b989ed02f906a54b38cf2eb16019799fb12eMeld Flock432622162023-05-28 20:30:458 mins ago santuyaelur.wallet  IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.055708559781 148.800191733
0xd18d2d60a35cb8b37bc97959617ca8854e1161f90e7c2cd38730768963edb8b0Meld Flock432622122023-05-28 20:30:378 mins ago0xbe0bcd648a784545cb379d201de6217e1fa638c2 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.059265601584 150.913266393
0xde96b4ec19ba9cc29ee275106a43e9b9c58e73e2507fb63847bb7aad8d6dff00Use Voucher432622042023-05-28 20:30:199 mins ago0xcf0e44b96ad7e6151c0a83107068a8bd071dad08 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.03395777989 152.122189028
0x7e17b8ae6d27f6c4f6c2887863f6b4720e56f7cbe7a6dae764a5666fe99cabc0Mint Pack432622012023-05-28 20:30:139 mins ago0x36d871ce7e8245d5dbb6e828c24e864ff7d7b684 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.034345630449147.488428949
0x8a5cd89a4625708391537ac01c003a141fea238470ea2325d006c861991fcfd6Mint Pack432621992023-05-28 20:30:099 mins ago0xad5d754778603e588210e6e15230b5158fe479ae IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.348996990351 148.59731456
0x35dbcf76417dd9d63bef7103ee1629c4eac3553a06fdcef8ddd46f1f07310667Meld Flock432621982023-05-28 20:30:079 mins ago0xbe0bcd648a784545cb379d201de6217e1fa638c2 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.060923050287 153.102995782
0x92055c34d20e0800e622a33eef8a9f9a032184b5050aefd2dadb7dc7dc6def61Mint Pack432621982023-05-28 20:30:079 mins ago zelthasoftware.zil  IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.034103353638 157.872009587
0x992154f9dfbbacf01acad9e77a291bec24b2181e8926992c24ef060d1c3a231dUse Voucher432621882023-05-28 20:29:459 mins ago0xcf0e44b96ad7e6151c0a83107068a8bd071dad08 IN  0xb66bf78cad7cbab51988ddc792652cbabdff76750 MATIC0.343135019725 144.593638835
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DuckyFamilyV1

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 22 : IERC721Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721Upgradeable is IERC165Upgradeable {
    /**
     * @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 caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

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

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

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165Upgradeable {
    /**
     * @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 3 of 22 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../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:
 *
 * ```
 * 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}:
 *
 * ```
 * 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.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    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 override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @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 override 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 override 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 override 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 `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @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 Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 4 of 22 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @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.
     *
     * _Available since v3.1._
     */
    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 `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 5 of 22 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @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 value {ERC20} uses, unless this function is
     * 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() public view virtual override returns (uint8) {
        return 18;
    }

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

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

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

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

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

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

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

File 6 of 22 : ERC20Burnable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20Burnable is Context, ERC20 {
    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        _spendAllowance(account, _msgSender(), amount);
        _burn(account, amount);
    }
}

File 7 of 22 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @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 decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

File 10 of 22 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @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,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode 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 {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]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        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);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode 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 {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        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]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        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.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // 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);
        }

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

        return (signer, RecoverError.NoError);
    }

    /**
     * @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) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

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

pragma solidity ^0.8.0;

import "./IERC165.sol";

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

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

pragma solidity ^0.8.0;

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

File 13 of 22 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * 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 + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * 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 10, 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 + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 14 of 22 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        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), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @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) {
        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] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

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

File 15 of 22 : DuckyFamilyV1.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

import '@openzeppelin/contracts/access/AccessControl.sol';
import '@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol';

import '../../../interfaces/IDuckyFamily.sol';
import '../../../interfaces/IDucklings.sol';
import '../Seeding.sol';
import '../Utils.sol';
import '../Genome.sol';
import './DuckyGenome.sol';

/**
 * @title DuckyFamilyV1
 *
 * @notice DuckyFamily contract defines rules of Ducky Family game, which is a card game similar to Happy Families and Uno games.
 * This game also incorporates vouchers as defined in IVoucher interface.
 *
 * DuckyFamily operates on Ducklings NFT, which is defined in a corresponding contract. DuckyFamily can mint, burn and query information about NFTs
 * by calling Ducklings contract.
 *
 * Users can buy NFT (card) packs of different size. When a pack is bought, a number of cards is generated and assigned to the user.
 * The packs can be bought with Duckies token, so user should approve DuckyFamily contract to spend Duckies on his behalf.
 *
 * Each card has a unique genome, which is a 256-bit number. The genome is a combination of different genes, which describe the card and its properties.
 * There are 3 types of cards introduced in this game, which are differentiated by the 'collection' gene: Duckling, Zombeak and Mythic.
 * Duckling and Zombeak NFTs have a class system, which is defined by 'rarity' gene: Common, Rare, Epic and Legendary.
 * Mythic NFTs are not part of the class system and are considered to be the most rare and powerful cards in the game.
 *
 * All cards have a set of generative genes, which are used to describe the card, its rarity and image.
 * There are 2 types of generative genes: with even and uneven chance for each value of that gene.
 * All values of even genes are generated with equal probability, while uneven genes have a higher chance for the first values and lower for the last values.
 * Thus, while even genes can describe the card, uneven can set the rarity of the card.
 *
 * Note: don't confuse 'rarity' gene with rarity of the card. 'Rarity' gene is a part of the game logic, while rarity of the card is a value this card represents.
 * Henceforth, if a 'Common' rarity gene card has uneven generative genes with high values (which means this card has a tiny chance to being generated),
 * then this card can be more rare than some 'Rare' rarity gene cards.
 * So, when we mean 'rarity' gene, we will use quotes, while when we mean rarity of the card, we will use it without quotes.
 *
 * Duckling are the main cards in the game, as they are the only way users can get Mythic cards.
 * However, users are not obliged to use every Duckling cards to help them get Mythic, they can improve them and collect the rarest ones.
 * Users can get Duckling cards from minting packs.
 *
 * Users can improve the 'rarity' of the card by melding them. Melding is a process of combining a flock of 5 cards to create a new one.
 * The new card will have the same 'collection' gene as the first card in the flock, but the 'rarity' gene will be incremented.
 * However, users must oblige to specific rules when melding cards:
 * 1. All cards in the flock must have the same 'collection' gene.
 * 2. All cards in the flock must have the same 'rarity' gene.
 * 3a. When melding Common cards, all cards in the flock must have either the same Color or Family gene values.
 * 3b. When melding Rare and Epic cards, all cards in the flock must have both the same Color and Family gene values.
 * 3c. When melding Legendary cards, all cards in the flock must have the same Color and different Family gene values.
 * 4. Mythic cards cannot be melded.
 * 5. Legendary Zombeak cards cannot be melded.
 *
 * Other generative genes of the melded card are not random, but are calculated from the genes of the source cards.
 * This process is called 'inheritance' and is the following:
 * 1. Each generative gene is inherited separately
 * 2. A gene has a high chance of being inherited from the first card in the flock, and this chance is lower for each next card in the flock.
 * 3. A gene has a mere chance of 'positive mutation', which sets inherited gene value to be bigger than the biggest value of this gene in the flock.
 *
 * Melding is not free and has a different cost for each 'rarity' of the cards being melded.
 *
 * Zombeak are secondary cards, that you can only get when melding mutates. There is a different chance (defined in Config section below) for each 'rarity' of the Duckling cards that are being melded,
 * that the melding result card will mutate to Zombeak. If the melding mutates, then the new card will have the same 'rarity' gene as the source cards.
 * This logic makes Zombeak cards more rare than some Duckling cards, as they can only be obtained by melding mutating.
 * However, Zombeak cards cannot be melded into Mythic, which means their main value is rarity.
 *
 * Mythic are the most rare and powerful cards in the game. They can only be obtained by melding Legendary Duckling cards with special rules described above.
 * The rarity of the Mythic card is defined by the 'UniqId' gene, which corresponds to the picture of the card. The higher the 'UniqId' gene value, the rarer the card.
 * The 'UniqId' value is correlated with the 'peculiarity' of the flock that creates the Mythic: the higher the peculiarity, the higher the 'UniqId' value.
 * Peculiarity of the card is a sum of all uneven gene values of this card, and peculiarity of the flock is a sum of peculiarities of all cards in the flock.
 *
 * Mythic cards give bonuses to their owned depending on their rarity. These bonuses will be revealed in the future, but they may include
 * free Yellow tokens (with vesting claim mechanism), an ability to change existing cards, stealing / fighting other cards, etc.
 */
contract DuckyFamilyV1 is IDuckyFamily, AccessControl, Seeding {
	using Genome for uint256;

	// Roles
	bytes32 public constant MAINTAINER_ROLE = keccak256('MAINTAINER_ROLE'); // can change minting and melding price

	address public issuer; // issuer of Vouchers

	// Store the vouchers to avoid replay attacks
	mapping(bytes32 => bool) internal _usedVouchers;

	// ------- Config -------

	/// @dev constants must be duplicated both here and in DuckyGenome as Solidity does not see Library constants as constants, see https://github.com/ethereum/solidity/issues/12248
	uint8 internal constant ducklingCollectionId = 0;
	uint8 internal constant zombeakCollectionId = 1;
	uint8 internal constant mythicCollectionId = 2;
	uint8 internal constant RARITIES_NUM = 4;

	uint8 internal constant collectionGeneIdx = Genome.COLLECTION_GENE_IDX;
	uint8 internal constant rarityGeneIdx = 1;
	uint8 internal constant flagsGeneIdx = Genome.FLAGS_GENE_IDX;
	// general genes start after Collection and Rarity
	uint8 internal constant generativeGenesOffset = 2;

	uint8 public constant MAX_PACK_SIZE = 50;
	uint8 public constant FLOCK_SIZE = 5;

	// number of values for each gene for Duckling and Zombeak collections
	uint8[][3] internal collectionsGeneValuesNum; // set in constructor

	// distribution type of each gene for Duckling and Zombeak collections (0 - even, 1 - uneven)
	uint32[3] internal collectionsGeneDistributionTypes = [
		2940, // reverse(001111101101) = 101101111100
		2940, // reverse(001111101101) = 101101111100
		107 // reverse(11010110) = 01101011
	];

	// peculiarity is a sum of uneven gene values for Ducklings
	uint16 internal maxPeculiarity;
	// mythic dispersion define the interval size in which UniqId value is generated
	uint8 internal constant MYTHIC_DISPERSION = 5;
	uint8 internal mythicAmount = 60;

	// chance of a Duckling of a certain rarity to be generated
	uint32[] internal rarityChances = [850, 120, 25, 5]; // per mil

	// chance of a Duckling of certain rarity to mutate to Zombeak while melding
	uint32[] internal collectionMutationChances = [150, 100, 50, 10]; // per mil

	uint32[] internal geneMutationChance = [955, 45]; // 4.5% to mutate gene value
	uint32[] internal geneInheritanceChances = [400, 300, 150, 100, 50]; // per mil

	// ------- Public values -------

	ERC20Burnable public duckiesContract;
	IDucklings public ducklingsContract;
	address public treasureVaultAddress;

	uint256 public mintPrice;
	uint256[RARITIES_NUM] public meldPrices; // [0] - melding Commons, [1] - melding Rares...

	// ------- Constructor -------

	/**
	 * @notice Sets Duckies, Ducklings and Treasure Vault addresses, minting and melding prices and other game config.
	 * @dev Grants DEFAULT_ADMIN_ROLE and MAINTAINER_ROLE to the deployer.
	 * @param duckiesAddress Address of Duckies ERC20 contract.
	 * @param ducklingsAddress Address of Ducklings ERC721 contract.
	 * @param treasureVaultAddress_ Address of Treasure Vault contract.
	 */
	constructor(address duckiesAddress, address ducklingsAddress, address treasureVaultAddress_) {
		_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
		_grantRole(MAINTAINER_ROLE, msg.sender);

		duckiesContract = ERC20Burnable(duckiesAddress);
		ducklingsContract = IDucklings(ducklingsAddress);
		treasureVaultAddress = treasureVaultAddress_;

		uint256 decimalsMultiplier = 10 ** duckiesContract.decimals();

		mintPrice = 50 * decimalsMultiplier;
		meldPrices = [
			100 * decimalsMultiplier,
			200 * decimalsMultiplier,
			500 * decimalsMultiplier,
			1000 * decimalsMultiplier
		];

		// Duckling genes: (Collection, Rarity), Color, Family, Body, Head, Eyes, Beak, Wings, FirstName, Temper, Skill, Habitat, Breed
		collectionsGeneValuesNum[0] = [4, 5, 10, 25, 30, 14, 10, 36, 16, 12, 5, 28];
		// Zombeak genes: (Collection, Rarity), Color, Family, Body, Head, Eyes, Beak, Wings, FirstName, Temper, Skill, Habitat, Breed
		collectionsGeneValuesNum[1] = [2, 3, 7, 6, 9, 7, 10, 36, 16, 12, 5, 28];
		// Mythic genes: (Collection, UniqId), Temper, Skill, Habitat, Breed, Birthplace, Quirk, Favorite Food, Favorite Color
		collectionsGeneValuesNum[2] = [16, 12, 5, 28, 5, 10, 8, 4];

		maxPeculiarity = DuckyGenome.calcConfigPeculiarity(
			collectionsGeneValuesNum[ducklingCollectionId],
			collectionsGeneDistributionTypes[ducklingCollectionId]
		);
	}

	// ------- Random -------

	/**
	 * @notice Sets the pepper for random generator.
	 * @dev Require MAINTAINER_ROLE to call. Pepper is a random data changed periodically by external entity.
	 * @param pepper New pepper.
	 */
	function setPepper(bytes32 pepper) external onlyRole(MAINTAINER_ROLE) {
		_setPepper(pepper);
	}

	// ------- Vouchers -------

	/**
	 * @notice Sets the issuer of Vouchers.
	 * @dev Require DEFAULT_ADMIN_ROLE to call.
	 * @param account Address of a new issuer.
	 */
	function setIssuer(address account) external onlyRole(DEFAULT_ADMIN_ROLE) {
		issuer = account;
	}

	/**
	 * @notice Use multiple Vouchers. Check the signature and invoke internal function for each voucher.
	 * @dev Vouchers are issued by the Back-End and signed by the issuer.
	 * @param vouchers Array of Vouchers to use.
	 * @param signature Vouchers signed by the issuer.
	 */
	function useVouchers(Voucher[] calldata vouchers, bytes calldata signature) external {
		Utils.requireCorrectSigner(abi.encode(vouchers), signature, issuer);
		for (uint8 i = 0; i < vouchers.length; i++) {
			_useVoucher(vouchers[i]);
		}
	}

	/**
	 * @notice Use a single Voucher. Check the signature and invoke internal function.
	 * @dev Vouchers are issued by the Back-End and signed by the issuer.
	 * @param voucher Voucher to use.
	 * @param signature Voucher signed by the issuer.
	 */
	function useVoucher(Voucher calldata voucher, bytes calldata signature) external {
		Utils.requireCorrectSigner(abi.encode(voucher), signature, issuer);
		_useVoucher(voucher);
	}

	/**
	 * @notice Check the validity of a voucher, decode voucher params and mint or meld tokens depending on voucher's type. Emits VoucherUsed event. Internal function.
	 * @dev Vouchers are issued by the Back-End and signed by the issuer.
	 * @param voucher Voucher to use.
	 */
	function _useVoucher(Voucher memory voucher) internal {
		_requireValidVoucher(voucher);

		_usedVouchers[voucher.voucherCodeHash] = true;

		// parse & process Voucher
		if (voucher.action == uint8(VoucherActions.MintPack)) {
			MintParams memory mintParams = abi.decode(voucher.encodedParams, (MintParams));

			// mintParams checks
			if (
				mintParams.to == address(0) ||
				mintParams.size == 0 ||
				mintParams.size > MAX_PACK_SIZE
			) revert InvalidMintParams(mintParams);

			_mintPackTo(mintParams.to, mintParams.size, mintParams.isTransferable);
		} else if (voucher.action == uint8(VoucherActions.MeldFlock)) {
			MeldParams memory meldParams = abi.decode(voucher.encodedParams, (MeldParams));

			// meldParams checks
			if (meldParams.owner == address(0) || meldParams.tokenIds.length != FLOCK_SIZE)
				revert InvalidMeldParams(meldParams);

			_meldOf(meldParams.owner, meldParams.tokenIds, meldParams.isTransferable);
		} else {
			revert InvalidVoucher(voucher);
		}

		emit VoucherUsed(
			voucher.beneficiary,
			voucher.action,
			voucher.voucherCodeHash,
			voucher.chainId
		);
	}

	/**
	 * @notice Check the validity of a voucher, reverts if invalid.
	 * @dev Voucher address must be this contract, beneficiary must be msg.sender, voucher must not be used before, voucher must not be expired.
	 * @param voucher Voucher to check.
	 */
	function _requireValidVoucher(Voucher memory voucher) internal view {
		if (_usedVouchers[voucher.voucherCodeHash])
			revert VoucherAlreadyUsed(voucher.voucherCodeHash);

		if (
			voucher.target != address(this) ||
			voucher.beneficiary != msg.sender ||
			block.timestamp > voucher.expire ||
			voucher.chainId != block.chainid
		) revert InvalidVoucher(voucher);
	}

	// -------- Config --------

	/**
	 * @notice Get the mint price in Duckies with decimals.
	 * @dev Get the mint price in Duckies with decimals.
	 * @return mintPrice Mint price in Duckies with decimals.
	 */
	function getMintPrice() external view returns (uint256) {
		return mintPrice;
	}

	/**
	 * @notice Set the mint price in Duckies without decimals.
	 * @dev Require MAINTAINER_ROLE to call.
	 * @param price Mint price in Duckies without decimals.
	 */
	function setMintPrice(uint256 price) external onlyRole(MAINTAINER_ROLE) {
		mintPrice = price * 10 ** duckiesContract.decimals();
	}

	/**
	 * @notice Get the meld price for each 'rarity' in Duckies with decimals.
	 * @dev Get the meld price for each 'rarity' in Duckies with decimals.
	 * @return meldPrices Array of meld prices in Duckies with decimals.
	 */
	function getMeldPrices() external view returns (uint256[RARITIES_NUM] memory) {
		return meldPrices;
	}

	/**
	 * @notice Set the meld price for each 'rarity' in Duckies without decimals.
	 * @dev Require MAINTAINER_ROLE to call.
	 * @param prices Array of meld prices in Duckies without decimals.
	 */
	function setMeldPrices(
		uint256[RARITIES_NUM] calldata prices
	) external onlyRole(MAINTAINER_ROLE) {
		for (uint8 i = 0; i < RARITIES_NUM; i++) {
			meldPrices[i] = prices[i] * 10 ** duckiesContract.decimals();
		}
	}

	/**
	 * @notice Get number of gene values for all collections and a number of different Mythic tokens.
	 * @dev Get number of gene values for all collections and a number of different Mythic tokens.
	 * @return collectionsGeneValuesNum Arrays of number of gene values for all collections and a mythic amount.
	 */
	function getCollectionsGeneValues() external view returns (uint8[][3] memory, uint8) {
		return (collectionsGeneValuesNum, mythicAmount);
	}

	/**
	 * @notice Get gene distribution types for all collections.
	 * @dev Get gene distribution types for all collections.
	 * @return collectionsGeneDistributionTypes Arrays of gene distribution types for all collections.
	 */
	function getCollectionsGeneDistributionTypes() external view returns (uint32[3] memory) {
		return collectionsGeneDistributionTypes;
	}

	/**
	 * @notice Set gene values number for each gene for Duckling collection.
	 * @dev Require DEFAULT_ADMIN_ROLE to call.
	 * @param duckingGeneValuesNum Array of gene values number for each gene for Duckling collection.
	 */
	function setDucklingGeneValues(
		uint8[] memory duckingGeneValuesNum
	) external onlyRole(DEFAULT_ADMIN_ROLE) {
		collectionsGeneValuesNum[0] = duckingGeneValuesNum;
		maxPeculiarity = DuckyGenome.calcConfigPeculiarity(
			collectionsGeneValuesNum[ducklingCollectionId],
			collectionsGeneDistributionTypes[ducklingCollectionId]
		);
	}

	/**
	 * @notice Set gene distribution types for Duckling collection.
	 * @dev Require DEFAULT_ADMIN_ROLE to call.
	 * @param ducklingGeneDistrTypes Gene distribution types for Duckling collection.
	 */
	function setDucklingGeneDistributionTypes(
		uint32 ducklingGeneDistrTypes
	) external onlyRole(DEFAULT_ADMIN_ROLE) {
		collectionsGeneDistributionTypes[0] = ducklingGeneDistrTypes;
		maxPeculiarity = DuckyGenome.calcConfigPeculiarity(
			collectionsGeneValuesNum[ducklingCollectionId],
			collectionsGeneDistributionTypes[ducklingCollectionId]
		);
	}

	/**
	 * @notice Set gene values number for each gene for Zombeak collection.
	 * @dev Require DEFAULT_ADMIN_ROLE to call.
	 * @param zombeakGeneValuesNum Array of gene values number for each gene for Duckling collection.
	 */
	function setZombeakGeneValues(
		uint8[] memory zombeakGeneValuesNum
	) external onlyRole(DEFAULT_ADMIN_ROLE) {
		collectionsGeneValuesNum[1] = zombeakGeneValuesNum;
	}

	/**
	 * @notice Set gene distribution types for Zombeak collection.
	 * @dev Require DEFAULT_ADMIN_ROLE to call.
	 * @param zombeakGeneDistrTypes Gene distribution types for Zombeak collection.
	 */
	function setZombeakGeneDistributionTypes(
		uint32 zombeakGeneDistrTypes
	) external onlyRole(DEFAULT_ADMIN_ROLE) {
		collectionsGeneDistributionTypes[1] = zombeakGeneDistrTypes;
	}

	/**
	 * @notice Set number of different Mythic tokens.
	 * @dev Require DEFAULT_ADMIN_ROLE to call.
	 * @param amount Number of different Mythic tokens.
	 */
	function setMythicAmount(uint8 amount) external onlyRole(DEFAULT_ADMIN_ROLE) {
		mythicAmount = amount;
	}

	/**
	 * @notice Set gene values number for each gene for Mythic collection.
	 * @dev Require DEFAULT_ADMIN_ROLE to call.
	 * @param mythicGeneValuesNum Array of gene values number for each gene for Mythic collection.
	 */
	function setMythicGeneValues(
		uint8[] memory mythicGeneValuesNum
	) external onlyRole(DEFAULT_ADMIN_ROLE) {
		collectionsGeneValuesNum[2] = mythicGeneValuesNum;
	}

	/**
	 * @notice Set gene distribution types for Mythic collection.
	 * @dev Require DEFAULT_ADMIN_ROLE to call.
	 * @param mythicGeneDistrTypes Gene distribution types for Mythic collection.
	 */
	function setMythicGeneDistributionTypes(
		uint32 mythicGeneDistrTypes
	) external onlyRole(DEFAULT_ADMIN_ROLE) {
		collectionsGeneDistributionTypes[2] = mythicGeneDistrTypes;
	}

	// ------- Mint -------

	/**
	 * @notice Mint a pack with `size` of Ducklings. Transfer Duckies from the sender to the TreasureVault.
	 * @dev `Size` must be less than or equal to `MAX_PACK_SIZE`.
	 * @param size Number of Ducklings in the pack.
	 */
	function mintPack(uint8 size) external {
		duckiesContract.transferFrom(msg.sender, treasureVaultAddress, mintPrice * size);
		_mintPackTo(msg.sender, size, true);
	}

	/**
	 * @notice Mint a pack with `amount` of Ducklings to `to` and set transferable flag for each token. Internal function.
	 * @dev `amount` must be less than or equal to `MAX_PACK_SIZE`.
	 * @param to Address to mint the pack to.
	 * @param amount Number of Ducklings in the pack.
	 * @param isTransferable Transferable flag for each token.
	 * @return tokenIds Array of minted token IDs.
	 */
	function _mintPackTo(
		address to,
		uint8 amount,
		bool isTransferable
	) internal returns (uint256[] memory tokenIds) {
		if (amount == 0 || amount > MAX_PACK_SIZE)
			revert MintingRulesViolated(ducklingCollectionId, amount);

		tokenIds = new uint256[](amount);
		uint256[] memory tokenGenomes = new uint256[](amount);

		for (uint256 i = 0; i < amount; i++) {
			tokenGenomes[i] = _generateGenome(ducklingCollectionId, _randomSeed()).setFlag(
				Genome.FLAG_TRANSFERABLE,
				isTransferable
			);
		}

		tokenIds = ducklingsContract.mintBatchTo(to, tokenGenomes);
	}

	/**
	 * @notice Generate genome for Duckling or Zombeak.
	 * @dev Generate and set all genes from a corresponding collection.
	 * @param collectionId Collection ID.
	 * @param seed Seed for randomization.
	 * @return genome Generated genome.
	 */
	function _generateGenome(uint8 collectionId, bytes32 seed) internal view returns (uint256) {
		if (collectionId != ducklingCollectionId && collectionId != zombeakCollectionId) {
			revert MintingRulesViolated(collectionId, 1);
		}

		(bytes3 bitSlice, bytes32 updatedSeed) = Utils.shiftSeedSlice(seed);

		uint256 genome;

		genome = genome.setGene(collectionGeneIdx, collectionId);
		genome = genome.setGene(rarityGeneIdx, Utils.randomWeightedNumber(rarityChances, bitSlice));
		genome = DuckyGenome.generateAndSetGenes(
			genome,
			collectionId,
			collectionsGeneValuesNum[collectionId],
			collectionsGeneDistributionTypes[collectionId],
			updatedSeed
		);
		genome = genome.setGene(Genome.MAGIC_NUMBER_GENE_IDX, Genome.BASE_MAGIC_NUMBER);

		return genome;
	}

	/**
	 * @notice Generate mythic genome based on melding `genomes`.
	 * @dev Calculates flock peculiarity, and randomizes UniqId corresponding to the peculiarity.
	 * @param genomes Array of genomes to meld into Mythic.
	 * @param maxPeculiarity_ Maximum peculiarity for the genomes collection config.
	 * @param mythicAmount_ Number of different Mythic tokens.
	 * @param seed Seed for randomization.
	 * @return genome Generated Mythic genome.
	 */
	function _generateMythicGenome(
		uint256[] memory genomes,
		uint16 maxPeculiarity_,
		uint16 mythicAmount_,
		bytes32 seed
	) internal view returns (uint256) {
		(bytes3 bitSlice, bytes32 updatedSeed) = Utils.shiftSeedSlice(seed);

		uint16 flockPeculiarity = 0;

		for (uint8 i = 0; i < genomes.length; i++) {
			flockPeculiarity += DuckyGenome.calcPeculiarity(
				genomes[i],
				uint8(collectionsGeneValuesNum[ducklingCollectionId].length),
				collectionsGeneDistributionTypes[ducklingCollectionId]
			);
		}

		uint16 maxSumPeculiarity = maxPeculiarity_ * uint16(genomes.length);
		uint16 maxUniqId = mythicAmount_ - 1;
		uint16 pivotalUniqId = uint16((uint64(flockPeculiarity) * maxUniqId) / maxSumPeculiarity); // multiply and then divide to avoid float numbers
		(uint16 leftEndUniqId, uint16 uniqIdSegmentLength) = DuckyGenome.calcUniqIdGenerationParams(
			pivotalUniqId,
			maxUniqId,
			MYTHIC_DISPERSION
		);

		uint16 uniqId = leftEndUniqId + uint16(Utils.randomNumber(bitSlice, uniqIdSegmentLength));

		uint256 genome;
		genome = genome.setGene(collectionGeneIdx, mythicCollectionId);
		genome = genome.setGene(uint8(MythicGenes.UniqId), uint8(uniqId));
		genome = DuckyGenome.generateAndSetGenes(
			genome,
			mythicCollectionId,
			collectionsGeneValuesNum[mythicCollectionId],
			collectionsGeneDistributionTypes[mythicCollectionId],
			updatedSeed
		);
		genome = genome.setGene(Genome.MAGIC_NUMBER_GENE_IDX, Genome.MYTHIC_MAGIC_NUMBER);

		return genome;
	}

	// ------- Meld -------

	/**
	 * @notice Meld tokens with `meldingTokenIds` into a new token. Calls internal function.
	 * @dev Meld tokens with `meldingTokenIds` into a new token.
	 * @param meldingTokenIds Array of token IDs to meld.
	 */
	function meldFlock(uint256[] calldata meldingTokenIds) external {
		// assume all tokens have the same rarity. This is checked later.
		uint256 meldPrice = meldPrices[
			ducklingsContract.getGenome(meldingTokenIds[0]).getGene(rarityGeneIdx)
		];
		duckiesContract.transferFrom(msg.sender, treasureVaultAddress, meldPrice);

		_meldOf(msg.sender, meldingTokenIds, true);
	}

	/**
	 * @notice Meld tokens with `meldingTokenIds` into a new token. Internal function.
	 * @dev Check `owner` is indeed the owner of `meldingTokenIds`. Burn NFTs with `meldingTokenIds`. Transfers Duckies to the TreasureVault.
	 * @param owner Address of the owner of the tokens to meld.
	 * @param meldingTokenIds Array of token IDs to meld.
	 * @param isTransferable Whether the new token is transferable.
	 * @return meldedTokenId ID of the new token.
	 */
	function _meldOf(
		address owner,
		uint256[] memory meldingTokenIds,
		bool isTransferable
	) internal returns (uint256) {
		if (meldingTokenIds.length != FLOCK_SIZE) revert MeldingRulesViolated(meldingTokenIds);
		if (!ducklingsContract.isOwnerOfBatch(owner, meldingTokenIds))
			revert MeldingRulesViolated(meldingTokenIds);

		uint256[] memory meldingGenomes = ducklingsContract.getGenomes(meldingTokenIds);
		DuckyGenome.requireGenomesSatisfyMelding(meldingGenomes);

		ducklingsContract.burnBatch(meldingTokenIds);

		uint256 meldedGenome = _meldGenomes(meldingGenomes, _randomSeed()).setFlag(
			Genome.FLAG_TRANSFERABLE,
			isTransferable
		);
		uint256 meldedTokenId = ducklingsContract.mintTo(owner, meldedGenome);

		emit Melded(owner, meldingTokenIds, meldedTokenId, block.chainid);

		return meldedTokenId;
	}

	/**
	 * @notice Meld `genomes` into a new genome.
	 * @dev Meld `genomes` into a new genome gene by gene. Set the corresponding collection
	 * @param genomes Array of genomes to meld.
	 * @param seed Seed for randomization.
	 * @return meldedGenome Melded genome.
	 */
	function _meldGenomes(uint256[] memory genomes, bytes32 seed) internal view returns (uint256) {
		uint8 collectionId = genomes[0].getGene(collectionGeneIdx);
		Rarities rarity = Rarities(genomes[0].getGene(rarityGeneIdx));

		(bytes3 bitSlice, bytes32 updatedSeed) = Utils.shiftSeedSlice(seed);

		// if melding Duckling, they can mutate or evolve into Mythic
		if (collectionId == ducklingCollectionId) {
			if (DuckyGenome.isCollectionMutating(rarity, collectionMutationChances, bitSlice)) {
				uint256 zombeakGenome = _generateGenome(zombeakCollectionId, updatedSeed);
				return zombeakGenome.setGene(rarityGeneIdx, uint8(rarity));
			}

			if (rarity == Rarities.Legendary) {
				return _generateMythicGenome(genomes, maxPeculiarity, mythicAmount, updatedSeed);
			}
		}

		uint256 meldedGenome;

		// set the same collection
		meldedGenome = meldedGenome.setGene(collectionGeneIdx, collectionId);
		// increase rarity
		meldedGenome = meldedGenome.setGene(rarityGeneIdx, genomes[0].getGene(rarityGeneIdx) + 1);

		uint8[] memory geneValuesNum = collectionsGeneValuesNum[collectionId];
		uint32 geneDistTypes = collectionsGeneDistributionTypes[collectionId];

		for (uint8 i = 0; i < geneValuesNum.length; i++) {
			(bitSlice, updatedSeed) = Utils.shiftSeedSlice(updatedSeed);
			uint8 geneValue = DuckyGenome.meldGenes(
				genomes,
				generativeGenesOffset + i,
				geneValuesNum[i],
				DuckyGenome.getDistributionType(geneDistTypes, i),
				geneMutationChance,
				geneInheritanceChances,
				bitSlice
			);
			meldedGenome = meldedGenome.setGene(generativeGenesOffset + i, geneValue);
		}

		// randomize Body for Common and Head for Rare for Ducklings
		if (collectionId == ducklingCollectionId) {
			(bitSlice, updatedSeed) = Utils.shiftSeedSlice(updatedSeed);
			if (rarity == Rarities.Common) {
				meldedGenome = DuckyGenome.generateAndSetGene(
					meldedGenome,
					uint8(GenerativeGenes.Body),
					geneValuesNum[uint8(GenerativeGenes.Body) - generativeGenesOffset],
					GeneDistributionTypes.Uneven,
					bitSlice
				);
			} else if (rarity == Rarities.Rare) {
				meldedGenome = DuckyGenome.generateAndSetGene(
					meldedGenome,
					uint8(GenerativeGenes.Head),
					geneValuesNum[uint8(GenerativeGenes.Head) - generativeGenesOffset],
					GeneDistributionTypes.Uneven,
					bitSlice
				);
			}
		}

		meldedGenome = meldedGenome.setGene(Genome.MAGIC_NUMBER_GENE_IDX, Genome.BASE_MAGIC_NUMBER);

		return meldedGenome;
	}
}

File 16 of 22 : DuckyGenome.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

import '@openzeppelin/contracts/utils/math/Math.sol';

import '../../../interfaces/IDuckyFamily.sol';
import '../Genome.sol';
import '../Utils.sol';

/**
 * @title DuckyGenome
 * @notice Library for generating Duckies genomes.
 * @dev Contains functions for generating Duckies genomes.
 */
library DuckyGenome {
	using Genome for uint256;

	/// @dev constants must be duplicated both here and in DuckyFamilyV1 as Solidity does not see Library constants as constants, see https://github.com/ethereum/solidity/issues/12248
	uint8 internal constant ducklingCollectionId = 0;
	uint8 internal constant zombeakCollectionId = 1;
	uint8 internal constant mythicCollectionId = 2;
	uint8 internal constant RARITIES_NUM = 4;

	uint8 internal constant collectionGeneIdx = Genome.COLLECTION_GENE_IDX;
	uint8 internal constant rarityGeneIdx = 1;
	uint8 internal constant flagsGeneIdx = Genome.FLAGS_GENE_IDX;
	uint8 internal constant generativeGenesOffset = 2;

	/**
	 * @notice Generates and sets genes to genome. Emits GenomeReturned event.
	 * @dev Generates and sets genes to genome. Emits GenomeReturned event.
	 * @param genome Genome to set genes to.
	 * @param collectionId Collection Id to generate genes for.
	 * @param geneValuesNum Number of gene values for each gene.
	 * @param geneDistributionTypes Gene distribution types.
	 * @param seed Seed for randomization.
	 */
	function generateAndSetGenes(
		uint256 genome,
		uint8 collectionId,
		uint8[] memory geneValuesNum,
		uint32 geneDistributionTypes,
		bytes32 seed
	) internal pure returns (uint256) {
		uint8 genesNum = uint8(geneValuesNum.length);
		bytes32 newSeed;

		// generate and set each gene
		for (uint8 i = 0; i < genesNum; i++) {
			IDuckyFamily.GeneDistributionTypes distrType = getDistributionType(
				geneDistributionTypes,
				i
			);
			bytes3 bitSlice;
			(bitSlice, newSeed) = Utils.shiftSeedSlice(seed);
			genome = generateAndSetGene(
				genome,
				generativeGenesOffset + i,
				geneValuesNum[i],
				distrType,
				bitSlice
			);
		}

		// set default values for Ducklings
		if (collectionId == ducklingCollectionId) {
			IDuckyFamily.Rarities rarity = IDuckyFamily.Rarities(genome.getGene(rarityGeneIdx));

			if (rarity == IDuckyFamily.Rarities.Common) {
				genome = genome.setGene(uint8(IDuckyFamily.GenerativeGenes.Body), 0);
				genome = genome.setGene(uint8(IDuckyFamily.GenerativeGenes.Head), 0);
			} else if (rarity == IDuckyFamily.Rarities.Rare) {
				genome = genome.setGene(uint8(IDuckyFamily.GenerativeGenes.Head), 0);
			}
		}

		return genome;
	}

	/**
	 * @notice Generate and set a gene with `geneIdx` to `genome`.
	 * @dev Generate and set a gene with `geneIdx` to `genome`.
	 * @param genome Genome to set a gene to.
	 * @param geneIdx Gene index.
	 * @param geneValuesNum Number of gene values.
	 * @param distrType Gene distribution type.
	 * @param bitSlice Random bit slice to generate a gene from.
	 * @return genome Genome with set gene.
	 */
	function generateAndSetGene(
		uint256 genome,
		uint8 geneIdx,
		uint8 geneValuesNum,
		IDuckyFamily.GeneDistributionTypes distrType,
		bytes3 bitSlice
	) internal pure returns (uint256) {
		uint8 geneValue;

		if (distrType == IDuckyFamily.GeneDistributionTypes.Even) {
			geneValue = uint8(Utils.randomNumber(bitSlice, geneValuesNum));
		} else {
			geneValue = uint8(generateUnevenGeneValue(geneValuesNum, bitSlice));
		}

		// gene with value 0 means it is a default value, thus this   \/
		genome = genome.setGene(geneIdx, geneValue + 1);

		return genome;
	}

	/**
	 * @notice Check that `genomes` satisfy melding rules. Reverts if not.
	 * @dev Check that `genomes` satisfy melding rules. Reverts if not.
	 * @param genomes Array of genomes to check.
	 */
	function requireGenomesSatisfyMelding(uint256[] memory genomes) internal pure {
		if (
			// equal collections
			!Genome.geneValuesAreEqual(genomes, collectionGeneIdx) ||
			// Rarities must be the same
			!Genome.geneValuesAreEqual(genomes, rarityGeneIdx) ||
			// not Mythic
			genomes[0].getGene(collectionGeneIdx) == mythicCollectionId
		) revert IDuckyFamily.IncorrectGenomesForMelding(genomes);

		IDuckyFamily.Rarities rarity = IDuckyFamily.Rarities(genomes[0].getGene(rarityGeneIdx));
		bool sameColors = Genome.geneValuesAreEqual(
			genomes,
			uint8(IDuckyFamily.GenerativeGenes.Color)
		);
		bool sameFamilies = Genome.geneValuesAreEqual(
			genomes,
			uint8(IDuckyFamily.GenerativeGenes.Family)
		);
		bool uniqueFamilies = Genome.geneValuesAreUnique(
			genomes,
			uint8(IDuckyFamily.GenerativeGenes.Family)
		);

		// specific melding rules
		if (rarity == IDuckyFamily.Rarities.Common) {
			// Common
			if (
				// cards must have the same Color OR the same Family
				!sameColors && !sameFamilies
			) revert IDuckyFamily.IncorrectGenomesForMelding(genomes);
		} else {
			// Rare, Epic
			if (rarity == IDuckyFamily.Rarities.Rare || rarity == IDuckyFamily.Rarities.Epic) {
				if (
					// cards must have the same Color AND the same Family
					!sameColors || !sameFamilies
				) revert IDuckyFamily.IncorrectGenomesForMelding(genomes);
			} else {
				// Legendary
				if (
					// not Legendary Zombeak
					genomes[0].getGene(collectionGeneIdx) == zombeakCollectionId ||
					// cards must have the same Color AND be of each Family
					!sameColors ||
					!uniqueFamilies
				) revert IDuckyFamily.IncorrectGenomesForMelding(genomes);
			}
		}
	}

	/**
	 * @notice Randomize if collection is mutating.
	 * @dev Randomize if collection is mutating.
	 * @param rarity Rarity of the collection.
	 * @param mutationChances Array of mutation chances for each rarity.
	 * @param bitSlice Bit slice to use for randomization.
	 * @return isMutating True if mutating, false otherwise.
	 */
	function isCollectionMutating(
		IDuckyFamily.Rarities rarity,
		uint32[] memory mutationChances,
		bytes3 bitSlice
	) internal pure returns (bool) {
		// check if mutating chance for this rarity is present
		if (mutationChances.length <= uint8(rarity)) {
			return false;
		}

		uint32 mutationPercentage = mutationChances[uint8(rarity)];
		// dynamic array is needed for `randomWeightedNumber()`
		uint32[] memory chances = new uint32[](2);
		chances[0] = mutationPercentage;
		chances[1] = 1000 - mutationPercentage; // 1000 as changes are represented in per mil
		return Utils.randomWeightedNumber(chances, bitSlice) == 0;
	}

	/**
	 * @notice Meld `gene` from `genomes` into a new gene value.
	 * @dev Meld `gene` from `genomes` into a new gene value. Gene mutation and inheritance are applied.
	 * @param genomes Array of genomes to meld.
	 * @param gene Gene to be meld.
	 * @param maxGeneValue Max gene value.
	 * @param geneDistrType Gene distribution type.
	 * @param mutationChance Mutation chance. Represented as [chance of no mutation, chance of mutation] in per mil.
	 * @param inheritanceChances Array of inheritance chances for each rarity.
	 * @param bitSlice Bit slice to use for randomization.
	 * @return geneValue Melded gene value.
	 */
	function meldGenes(
		uint256[] memory genomes,
		uint8 gene,
		uint8 maxGeneValue,
		IDuckyFamily.GeneDistributionTypes geneDistrType,
		uint32[] memory mutationChance,
		uint32[] memory inheritanceChances,
		bytes3 bitSlice
	) internal pure returns (uint8) {
		// gene mutation
		if (
			geneDistrType == IDuckyFamily.GeneDistributionTypes.Uneven &&
			Utils.randomWeightedNumber(mutationChance, bitSlice) == 1
		) {
			uint8 maxPresentGeneValue = Genome.maxGene(genomes, gene);
			return maxPresentGeneValue == maxGeneValue ? maxGeneValue : maxPresentGeneValue + 1;
		}

		// gene inheritance
		uint8 inheritanceIdx = Utils.randomWeightedNumber(inheritanceChances, bitSlice);
		return genomes[inheritanceIdx].getGene(gene);
	}

	// ------- Helpers -------

	/**
	 * @notice Get gene distribution type.
	 * @dev Get gene distribution type.
	 * @param distributionTypes Distribution types.
	 * @param idx Index of the gene.
	 * @return Gene distribution type.
	 */
	function getDistributionType(
		uint32 distributionTypes,
		uint8 idx
	) internal pure returns (IDuckyFamily.GeneDistributionTypes) {
		return
			distributionTypes & (1 << idx) == 0
				? IDuckyFamily.GeneDistributionTypes.Even
				: IDuckyFamily.GeneDistributionTypes.Uneven;
	}

	/**
	 * @notice Generate uneven gene value given the maximum number of values.
	 * @dev Generate uneven gene value using reciprocal distribution described below.
	 * @param valuesNum Maximum number of gene values.
	 * @param bitSlice Bit slice to use for randomization.
	 * @return geneValue Gene value.
	 */
	function generateUnevenGeneValue(
		uint8 valuesNum,
		bytes3 bitSlice
	) internal pure returns (uint8) {
		// using reciprocal distribution
		// gene value is selected as ceil[(2N/(x+1))-N],
		// where x is random number between 0 and 1
		// Because of shape of reciprocal graph,
		// evenly distributed x values will result in unevenly distributed y values.

		// N - number of gene values
		uint256 N = uint256(valuesNum);
		// Generates number from 1 to 10^6
		uint256 x = 1 + Utils.randomNumber(bitSlice, 1_000_000);
		// Calculates uneven distributed y, value of y is between 0 and N
		uint256 y = (2 * N * 1_000) / (Math.sqrt(x) + 1_000) - N;
		return uint8(y);
	}

	/**
	 * @notice Calculate max peculiarity for a supplied config.
	 * @dev Sum up number of uneven gene values.
	 * @param geneValuesNum Array of number of gene values for each gene.
	 * @param geneDistrTypes Gene distribution types.
	 * @return maxPeculiarity Max peculiarity.
	 */
	function calcConfigPeculiarity(
		uint8[] memory geneValuesNum,
		uint32 geneDistrTypes
	) internal pure returns (uint16) {
		uint16 sum = 0;

		uint8 genesNum = uint8(geneValuesNum.length);
		for (uint8 i = 0; i < genesNum; i++) {
			if (
				getDistributionType(geneDistrTypes, i) == IDuckyFamily.GeneDistributionTypes.Uneven
			) {
				// add number of values and not actual values as actual values start with 1, which means number of values and actual values are equal
				sum += geneValuesNum[i];
			}
		}

		return sum;
	}

	/**
	 * @notice Calculate peculiarity for a given genome.
	 * @dev Sum up number of uneven gene values.
	 * @param genome Genome.
	 * @param genesNum Number of genes.
	 * @param geneDistrTypes Gene distribution types.
	 * @return peculiarity Peculiarity.
	 */
	function calcPeculiarity(
		uint256 genome,
		uint8 genesNum,
		uint32 geneDistrTypes
	) internal pure returns (uint16) {
		uint16 sum = 0;

		for (uint8 i = 0; i < genesNum; i++) {
			if (
				getDistributionType(geneDistrTypes, i) == IDuckyFamily.GeneDistributionTypes.Uneven
			) {
				// add number of values and not actual values as actual values start with 1, which means number of values and actual values are equal
				sum += genome.getGene(i + generativeGenesOffset);
			}
		}

		return sum;
	}

	/**
	 * @notice Calculate `leftEndUniqId` and `uniqIdSegmentLength` for UniqId generation.
	 * @dev Then UniqId is generated by adding a random number [0, `uniqIdSegmentLength`) to `leftEndUniqId`.
	 * @param pivotalUniqId Pivotal UniqId.
	 * @param maxUniqId Max UniqId.
	 * @param mythicDispersion Half of the segment length in which mythic UniqIds are generated.
	 * @return leftEndUniqId Left end of the UniqId segment.
	 * @return uniqIdSegmentLength Length of the UniqId segment.
	 */
	function calcUniqIdGenerationParams(
		uint16 pivotalUniqId,
		uint16 maxUniqId,
		uint16 mythicDispersion
	) internal pure returns (uint16 leftEndUniqId, uint16 uniqIdSegmentLength) {
		if (pivotalUniqId < mythicDispersion) {
			// mythic id range overlaps with left dispersion border
			leftEndUniqId = 0;
			uniqIdSegmentLength = pivotalUniqId + mythicDispersion;
		} else if (maxUniqId < pivotalUniqId + mythicDispersion) {
			// mythic id range overlaps with right dispersion border
			leftEndUniqId = pivotalUniqId - mythicDispersion;
			uniqIdSegmentLength = maxUniqId - leftEndUniqId + 1; // +1 to include right border, where the last UniqId is located
		} else {
			// mythic id range does not overlap with dispersion borders
			leftEndUniqId = pivotalUniqId - mythicDispersion;
			uniqIdSegmentLength = 2 * mythicDispersion;
		}
	}
}

File 17 of 22 : Genome.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

/**
 * @title Genome
 *
 * @notice The library to work with NFT genomes.
 *
 * Genome is a number with a special structure that defines Duckling genes.
 * All genes are packed consequently in the reversed order in the Genome, meaning the first gene being stored in the last Genome bits.
 * Each gene takes up the block of 8 bits in genome, thus having 256 possible values.
 *
 * Example of genome, following genes Rarity, Head and Body are defined:
 *
 * 00000001|00000010|00000011
 *   Body    Head     Rarity
 *
 * This genome can be represented in uint24 as 66051.
 * Genes have the following values: Body = 1, Head = 2, Rarity = 3.
 */
library Genome {
	/// @notice Number of bits each gene constitutes. Thus, each gene can have 2^8 = 256 possible values.
	uint8 public constant BITS_PER_GENE = 8;

	uint8 public constant COLLECTION_GENE_IDX = 0;

	// Flags
	/// @notice Reserve 30th gene for bool flags, which are stored as a bit field.
	uint8 public constant FLAGS_GENE_IDX = 30;
	uint8 public constant FLAG_TRANSFERABLE = 1; // 0b0000_0001

	// Magic number
	/// @notice Reserve 31th gene for magic number, which is used as an extension for genomes.
	/// Genomes with wrong extension are considered invalid.
	uint8 public constant MAGIC_NUMBER_GENE_IDX = 31;
	uint8 public constant BASE_MAGIC_NUMBER = 209; // Ð
	uint8 public constant MYTHIC_MAGIC_NUMBER = 210; // Ð + 1

	/**
	 * @notice Read flags gene from genome.
	 * @dev Read flags gene from genome.
	 * @param self Genome to get flags gene from.
	 * @return flags Flags gene.
	 */
	function getFlags(uint256 self) internal pure returns (uint8) {
		return getGene(self, FLAGS_GENE_IDX);
	}

	/**
	 * @notice Read specific bit mask flag from genome.
	 * @dev Read specific bit mask flag from genome.
	 * @param self Genome to read flag from.
	 * @param flag Bit mask flag to read.
	 * @return value Value of the flag.
	 */
	function getFlag(uint256 self, uint8 flag) internal pure returns (bool) {
		return getGene(self, FLAGS_GENE_IDX) & flag > 0;
	}

	/**
	 * @notice Set specific bit mask flag in genome.
	 * @dev Set specific bit mask flag in genome.
	 * @param self Genome to set flag in.
	 * @param flag Bit mask flag to set.
	 * @param value Value of the flag.
	 * @return genome Genome with the flag set.
	 */
	function setFlag(uint256 self, uint8 flag, bool value) internal pure returns (uint256) {
		uint8 flags = getGene(self, FLAGS_GENE_IDX);
		if (value) {
			flags |= flag;
		} else {
			flags &= ~flag;
		}
		return setGene(self, FLAGS_GENE_IDX, flags);
	}

	/**
	 * @notice Set `value` to `gene` in genome.
	 * @dev Set `value` to `gene` in genome.
	 * @param self Genome to set gene in.
	 * @param gene Gene to set.
	 * @param value Value to set.
	 * @return genome Genome with the gene set.
	 */
	function setGene(
		uint256 self,
		uint8 gene,
		// by specifying uint8 we set maxCap for gene values, which is 256
		uint8 value
	) internal pure returns (uint256) {
		// number of bytes from genome's rightmost and geneBlock's rightmost
		// NOTE: maximum index of a gene is actually uint5
		uint8 shiftingBy = gene * BITS_PER_GENE;

		// remember genes we will shift off
		uint256 shiftedPart = self & ((1 << shiftingBy) - 1);

		// shift right so that genome's rightmost bit is the geneBlock's rightmost
		self >>= shiftingBy;

		// clear previous gene value by shifting it off
		self >>= BITS_PER_GENE;
		self <<= BITS_PER_GENE;

		// update gene's value
		self += value;

		// reserve space for restoring previously shifted off values
		self <<= shiftingBy;

		// restore previously shifted off values
		self += shiftedPart;

		return self;
	}

	/**
	 * @notice Get `gene` value from genome.
	 * @dev Get `gene` value from genome.
	 * @param self Genome to get gene from.
	 * @param gene Gene to get.
	 * @return geneValue Gene value.
	 */
	function getGene(uint256 self, uint8 gene) internal pure returns (uint8) {
		// number of bytes from genome's rightmost and geneBlock's rightmost
		// NOTE: maximum index of a gene is actually uint5
		uint8 shiftingBy = gene * BITS_PER_GENE;

		uint256 temp = self >> shiftingBy;
		return uint8(temp & ((1 << BITS_PER_GENE) - 1));
	}

	/**
	 * @notice Get largest value of a `gene` in `genomes`.
	 * @dev Get largest value of a `gene` in `genomes`.
	 * @param genomes Genomes to get gene from.
	 * @param gene Gene to get.
	 * @return maxValue Largest value of a `gene` in `genomes`.
	 */
	function maxGene(uint256[] memory genomes, uint8 gene) internal pure returns (uint8) {
		uint8 maxValue = 0;

		for (uint256 i = 0; i < genomes.length; i++) {
			uint8 geneValue = getGene(genomes[i], gene);
			if (maxValue < geneValue) {
				maxValue = geneValue;
			}
		}

		return maxValue;
	}

	/**
	 * @notice Check if values of `gene` in `genomes` are equal.
	 * @dev Check if values of `gene` in `genomes` are equal.
	 * @param genomes Genomes to check.
	 * @param gene Gene to check.
	 * @return isEqual True if values of `gene` in `genomes` are equal, false otherwise.
	 */
	function geneValuesAreEqual(uint256[] memory genomes, uint8 gene) internal pure returns (bool) {
		uint8 geneValue = getGene(genomes[0], gene);

		for (uint256 i = 1; i < genomes.length; i++) {
			if (getGene(genomes[i], gene) != geneValue) {
				return false;
			}
		}

		return true;
	}

	/**
	 * @notice Check if values of `gene` in `genomes` are unique.
	 * @dev Check if values of `gene` in `genomes` are unique.
	 * @param genomes Genomes to check.
	 * @param gene Gene to check.
	 * @return isUnique True if values of `gene` in `genomes` are unique, false otherwise.
	 */
	function geneValuesAreUnique(
		uint256[] memory genomes,
		uint8 gene
	) internal pure returns (bool) {
		uint256 valuesPresentBitfield = 1 << getGene(genomes[0], gene);

		for (uint256 i = 1; i < genomes.length; i++) {
			if (valuesPresentBitfield & (1 << getGene(genomes[i], gene)) != 0) {
				return false;
			}
			valuesPresentBitfield |= 1 << getGene(genomes[i], gene);
		}

		return true;
	}
}

File 18 of 22 : Seeding.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

/**
 * @title Seeding
 * @notice A contract that provides seeds for pseudo random number generation.
 * Seed is created from the salt, pepper, nonce, sender address, and block timestamp.
 * Seed is divided into 32 bit slices, and each slice is later used to generate a random number.
 * Seed user must keep track of used bit slices to avoid reusing them.
 * Salt is a data based on block timestamp and msg sender, and is calculated every time a seed is generated.
 * Pepper is a random data changed periodically by external entity.
 * Nonce is incremented every time a random number is generated.
 */
contract Seeding {
	bytes32 private salt;
	bytes32 private pepper;
	uint256 private nonce;

	/**
	 * @notice Sets the pepper.
	 * @dev Pepper is a random data changed periodically by external entity.
	 * @param newPepper New pepper.
	 */
	function _setPepper(bytes32 newPepper) internal {
		pepper = newPepper;
	}

	/**
	 * @notice Creates a new seed based on the salt, pepper, nonce, sender address, and block timestamp.
	 * @dev Creates a new seed based on the salt, pepper, nonce, sender address, and block timestamp.
	 * @return New seed.
	 */
	function _randomSeed() internal returns (bytes32) {
		// use old salt to generate a new one, so that user's predictions are invalid after function that uses random is called
		salt = keccak256(abi.encode(salt, msg.sender, block.timestamp));
		unchecked {
			nonce++;
		}

		return keccak256(abi.encode(salt, pepper, nonce, msg.sender, block.timestamp));
	}
}

File 19 of 22 : Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';

import '../../interfaces/IVoucher.sol';

/**
 * @title Utils
 * @notice Utility functions for games, that include pseudo random number generation, signature verification, etc.
 *
 * Pseudo random number generation is based on the bitSlices, which are part of a seed created in `Seeding.sol`.
 */
library Utils {
	using ECDSA for bytes32;

	/**
	 * @notice Invalid weights error while trying to generate a weighted random number.
	 * @param weights Empty weights array.
	 */
	error InvalidWeights(uint32[] weights);

	/**
	 * @notice Perform circular shift on the seed by 3 bytes to the left, and returns the shifted slice and the updated seed.
	 * @dev User of this contract must keep track of used bit slices to avoid reusing them.
	 * @param seed Seed to shift and extract the shifted slice from.
	 * @return bitSlice Shifted bit slice.
	 * @return updatedSeed Shifted seed.
	 */
	function shiftSeedSlice(bytes32 seed) internal pure returns (bytes3, bytes32) {
		bytes3 slice = bytes3(seed);
		return (slice, (seed << 24) | (bytes32(slice) >> 232));
	}

	/**
	 * @notice Extracts a number from the bit slice in range [0, max).
	 * @dev Extracts a number from the bit slice in range [0, max).
	 * @param bitSlice Bit slice to extract the number from.
	 * @param max Max number to extract.
	 * @return Extracted number in range [0, max).
	 */
	function randomNumber(bytes3 bitSlice, uint24 max) internal pure returns (uint24) {
		return uint24(bitSlice) % max;
	}

	/**
	 * @notice Generates a weighted random number in range [0, weights.length).
	 * @dev Number `x` is generated with probability `weights[x] / sum(weights)`.
	 * @param weights Array of weights.
	 * @return Random number in range [0, weights.length).
	 */
	function randomWeightedNumber(
		uint32[] memory weights, // chances are represented in per mil
		bytes3 bitSlice
	) internal pure returns (uint8) {
		uint24 wsum = uint24(sum(weights));

		// no sense in empty weights array
		if (weights.length == 0 || wsum == 0) revert InvalidWeights(weights);

		uint256 rnum = randomNumber(bitSlice, wsum);

		uint256 segmentRightBoundary = 0;

		for (uint8 i = 0; i < weights.length; i++) {
			segmentRightBoundary += weights[i];
			if (rnum < segmentRightBoundary) {
				return i;
			}
		}

		// execution should never reach this
		return uint8(weights.length - 1);
	}

	/**
	 * @notice Calculates sum of all elements in array.
	 * @dev Calculates sum of all elements in array.
	 * @param numbers Array of numbers.
	 * @return res Sum of all elements in array.
	 */
	function sum(uint32[] memory numbers) internal pure returns (uint256 res) {
		for (uint256 i = 0; i < numbers.length; i++) res += numbers[i];
	}

	/**
	 * @notice Check that `signatures is `encodedData` signed by `signer`. Reverts if not.
	 * @dev Check that `signatures is `encodedData` signed by `signer`. Reverts if not.
	 * @param encodedData Data to check.
	 * @param signature Signature to check.
	 * @param signer Address of the signer.
	 */
	function requireCorrectSigner(
		bytes memory encodedData,
		bytes memory signature,
		address signer
	) internal pure {
		address actualSigner = keccak256(encodedData).toEthSignedMessageHash().recover(signature);
		if (actualSigner != signer) revert IVoucher.IncorrectSigner(signer, actualSigner);
	}
}

File 20 of 22 : IDucklings.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

import '@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol';

/**
 * @title IDucklings
 * @notice This interface defines the Ducklings ERC721-compatible contract,
 * which provides basic functionality for minting, burning and querying information about the tokens.
 */
interface IDucklings is IERC721Upgradeable {
	/**
	 * @notice Token not transferable error. Is used when trying to transfer a token that is not transferable.
	 * @param tokenId Token Id that is not transferable.
	 */
	error TokenNotTransferable(uint256 tokenId);
	/**
	 * @notice Invalid magic number error. Is used when trying to mint a token with an invalid magic number.
	 * @param magicNumber Magic number that is invalid.
	 */
	error InvalidMagicNumber(uint8 magicNumber);

	struct Duckling {
		uint256 genome;
		uint64 birthdate;
	}

	// events
	/**
	 * @notice Minted event. Is emitted when a token is minted.
	 * @param to Address of the token owner.
	 * @param tokenId Id of the minted token.
	 * @param genome Genome of the minted token.
	 * @param birthdate Birthdate of the minted token.
	 * @param chainId Id of the chain where the token was minted.
	 */
	event Minted(address to, uint256 tokenId, uint256 genome, uint64 birthdate, uint256 chainId);

	/**
	 * @notice Check whether `account` is owner of `tokenId`.
	 * @dev Revert if `account` is address(0) or `tokenId` does not exist.
	 * @param account Address to check.
	 * @param tokenId Token Id to check.
	 * @return isOwnerOf True if `account` is owner of `tokenId`, false otherwise.
	 */
	function isOwnerOf(address account, uint256 tokenId) external view returns (bool);

	/**
	 * @notice Check whether `account` is owner of `tokenIds`.
	 * @dev Revert if `account` is address(0) or any of `tokenIds` do not exist.
	 * @param account Address to check.
	 * @param tokenIds Token Ids to check.
	 * @return isOwnerOfBatch True if `account` is owner of `tokenIds`, false otherwise.
	 */
	function isOwnerOfBatch(
		address account,
		uint256[] calldata tokenIds
	) external view returns (bool);

	/**
	 * @notice Get genome of `tokenId`.
	 * @dev Revert if `tokenId` does not exist.
	 * @param tokenId Token Id to get the genome of.
	 * @return genome Genome of `tokenId`.
	 */
	function getGenome(uint256 tokenId) external view returns (uint256);

	/**
	 * @notice Get genomes of `tokenIds`.
	 * @dev Revert if any of `tokenIds` do not exist.
	 * @param tokenIds Token Ids to get the genomes of.
	 * @return genomes Genomes of `tokenIds`.
	 */
	function getGenomes(uint256[] calldata tokenIds) external view returns (uint256[] memory);

	/**
	 * @notice Mint token with `genome` to `to`. Emits Minted event.
	 * @dev Revert if `to` is address(0) or `genome` has wrong magic number.
	 * @param to Address to mint token to.
	 * @param genome Genome of the token to mint.
	 * @return tokenId Id of the minted token.
	 */
	function mintTo(address to, uint256 genome) external returns (uint256);

	/**
	 * @notice Mint tokens with `genomes` to `to`. Emits Minted event for each token.
	 * @dev Revert if `to` is address(0) or any of `genomes` has wrong magic number.
	 * @param to Address to mint tokens to.
	 * @param genomes Genomes of the tokens to mint.
	 * @return tokenIds Ids of the minted tokens.
	 */
	function mintBatchTo(
		address to,
		uint256[] calldata genomes
	) external returns (uint256[] memory);

	/**
	 * @notice Burn token with `tokenId`.
	 * @dev Revert if `tokenId` does not exist.
	 * @param tokenId Id of the token to burn.
	 */
	function burn(uint256 tokenId) external;

	/**
	 * @notice Burn tokens with `tokenIds`.
	 * @dev Revert if any of `tokenIds` do not exist.
	 * @param tokenIds Ids of the tokens to burn.
	 */
	function burnBatch(uint256[] calldata tokenIds) external;
}

File 21 of 22 : IDuckyFamily.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

import './IVoucher.sol';

interface IDuckyFamily is IVoucher {
	// Errors
	error InvalidMintParams(MintParams mintParams);
	error InvalidMeldParams(MeldParams meldParams);

	error MintingRulesViolated(uint8 collectionId, uint8 amount);
	error MeldingRulesViolated(uint256[] tokenIds);
	error IncorrectGenomesForMelding(uint256[] genomes);

	// Events
	event Melded(address owner, uint256[] meldingTokenIds, uint256 meldedTokenId, uint256 chainId);

	// Vouchers
	enum VoucherActions {
		MintPack,
		MeldFlock
	}

	struct MintParams {
		address to;
		uint8 size;
		bool isTransferable;
	}

	struct MeldParams {
		address owner;
		uint256[] tokenIds;
		bool isTransferable;
	}

	// DuckyFamily

	// for now, Solidity does not support starting value for enum
	// enum Collections {
	// 	Duckling = 0,
	// 	Zombeak,
	// 	Mythic
	// }

	enum Rarities {
		Common,
		Rare,
		Epic,
		Legendary
	}

	enum GeneDistributionTypes {
		Even,
		Uneven
	}

	enum GenerativeGenes {
		Collection,
		Rarity,
		Color,
		Family,
		Body,
		Head
	}

	enum MythicGenes {
		Collection,
		UniqId
	}

	// Config
	function getMintPrice() external view returns (uint256);

	function getMeldPrices() external view returns (uint256[4] memory);

	function getCollectionsGeneValues() external view returns (uint8[][3] memory, uint8);

	function getCollectionsGeneDistributionTypes() external view returns (uint32[3] memory);

	// Mint and Meld
	function mintPack(uint8 size) external;

	function meldFlock(uint256[] calldata meldingTokenIds) external;
}

File 22 of 22 : IVoucher.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

/**
 * @notice Interface describing Voucher for redeeming game items
 *
 * @dev The Voucher type must have a strict implementation on backend
 *
 * A Voucher is a document signed from the server IssuerKey and allows the execution
 * of actions on the game generally for creating game items, such as Booster Packs, Meld or reward tokens
 *
 */
interface IVoucher {
	/**
	 * @notice Custom error specifying that voucher has already been used.
	 * @param voucherCodeHash Hash of the code of the voucher that has been used.
	 */
	error VoucherAlreadyUsed(bytes32 voucherCodeHash);

	/**
	 * @notice Custom error specifying that voucher has not passed general voucher checks and is invalid.
	 * @param voucher Voucher that is invalid.
	 */
	error InvalidVoucher(Voucher voucher);

	/**
	 * @notice Custom error specifying that the message was expected to be signed by `expected` address, but was signed by `actual`.
	 * @param expected Expected address to have signed the message.
	 * @param actual Actual address that has signed the message.
	 */
	error IncorrectSigner(address expected, address actual);

	/**
	 * @dev Build and encode the Voucher from server side
	 *
	 * Voucher structure will be valid only in chainId until expire timestamp
	 * the beneficiary MUST be the same as the user redeeming the Voucher.
	 *
	 */
	struct Voucher {
		address target; // contract address which the voucher is meant for
		uint8 action; // voucher type defined by the implementation
		address beneficiary; // beneficiary account which voucher will redeem to
		address referrer; // address of the parent
		uint64 expire; // expiration time in seconds UTC
		uint32 chainId; // chain id of the voucher
		bytes32 voucherCodeHash; // hash of voucherCode
		bytes encodedParams; // voucher type specific encoded params
	}

	/**
	 * @notice Use vouchers that were issued and signed by the Back-end to receive game items.
	 * @param vouchers Vouchers issued by the Back-end.
	 * @param signature Vouchers signed by the Back-end.
	 */
	function useVouchers(Voucher[] calldata vouchers, bytes calldata signature) external;

	/**
	 * @notice Use the voucher that was signed by the Back-end to receive game items.
	 * @param voucher Voucher issued by the Back-end.
	 * @param signature Voucher signed by the Back-end.
	 */
	function useVoucher(Voucher calldata voucher, bytes calldata signature) external;

	/**
	 * @notice Event specifying that a voucher has been used.
	 * @param wallet Wallet that used a voucher.
	 * @param action The action of the voucher used.
	 * @param voucherCodeHash The code hash of the voucher used.
	 * @param chainId Id of the chain the voucher was used on.
	 */
	event VoucherUsed(address wallet, uint8 action, bytes32 voucherCodeHash, uint32 chainId);
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"duckiesAddress","type":"address"},{"internalType":"address","name":"ducklingsAddress","type":"address"},{"internalType":"address","name":"treasureVaultAddress_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256[]","name":"genomes","type":"uint256[]"}],"name":"IncorrectGenomesForMelding","type":"error"},{"inputs":[{"internalType":"address","name":"expected","type":"address"},{"internalType":"address","name":"actual","type":"address"}],"name":"IncorrectSigner","type":"error"},{"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"bool","name":"isTransferable","type":"bool"}],"internalType":"struct IDuckyFamily.MeldParams","name":"meldParams","type":"tuple"}],"name":"InvalidMeldParams","type":"error"},{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint8","name":"size","type":"uint8"},{"internalType":"bool","name":"isTransferable","type":"bool"}],"internalType":"struct IDuckyFamily.MintParams","name":"mintParams","type":"tuple"}],"name":"InvalidMintParams","type":"error"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint8","name":"action","type":"uint8"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint64","name":"expire","type":"uint64"},{"internalType":"uint32","name":"chainId","type":"uint32"},{"internalType":"bytes32","name":"voucherCodeHash","type":"bytes32"},{"internalType":"bytes","name":"encodedParams","type":"bytes"}],"internalType":"struct IVoucher.Voucher","name":"voucher","type":"tuple"}],"name":"InvalidVoucher","type":"error"},{"inputs":[{"internalType":"uint32[]","name":"weights","type":"uint32[]"}],"name":"InvalidWeights","type":"error"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"MeldingRulesViolated","type":"error"},{"inputs":[{"internalType":"uint8","name":"collectionId","type":"uint8"},{"internalType":"uint8","name":"amount","type":"uint8"}],"name":"MintingRulesViolated","type":"error"},{"inputs":[{"internalType":"bytes32","name":"voucherCodeHash","type":"bytes32"}],"name":"VoucherAlreadyUsed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"meldingTokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"meldedTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"Melded","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint8","name":"action","type":"uint8"},{"indexed":false,"internalType":"bytes32","name":"voucherCodeHash","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"chainId","type":"uint32"}],"name":"VoucherUsed","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FLOCK_SIZE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAINTAINER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PACK_SIZE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"duckiesContract","outputs":[{"internalType":"contract ERC20Burnable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ducklingsContract","outputs":[{"internalType":"contract IDucklings","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollectionsGeneDistributionTypes","outputs":[{"internalType":"uint32[3]","name":"","type":"uint32[3]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollectionsGeneValues","outputs":[{"internalType":"uint8[][3]","name":"","type":"uint8[][3]"},{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMeldPrices","outputs":[{"internalType":"uint256[4]","name":"","type":"uint256[4]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMintPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"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":[],"name":"issuer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"meldingTokenIds","type":"uint256[]"}],"name":"meldFlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"meldPrices","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"size","type":"uint8"}],"name":"mintPack","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","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":"uint32","name":"ducklingGeneDistrTypes","type":"uint32"}],"name":"setDucklingGeneDistributionTypes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"duckingGeneValuesNum","type":"uint8[]"}],"name":"setDucklingGeneValues","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setIssuer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[4]","name":"prices","type":"uint256[4]"}],"name":"setMeldPrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"name":"setMintPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"amount","type":"uint8"}],"name":"setMythicAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"mythicGeneDistrTypes","type":"uint32"}],"name":"setMythicGeneDistributionTypes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"mythicGeneValuesNum","type":"uint8[]"}],"name":"setMythicGeneValues","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"pepper","type":"bytes32"}],"name":"setPepper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"zombeakGeneDistrTypes","type":"uint32"}],"name":"setZombeakGeneDistributionTypes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"zombeakGeneValuesNum","type":"uint8[]"}],"name":"setZombeakGeneValues","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasureVaultAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint8","name":"action","type":"uint8"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint64","name":"expire","type":"uint64"},{"internalType":"uint32","name":"chainId","type":"uint32"},{"internalType":"bytes32","name":"voucherCodeHash","type":"bytes32"},{"internalType":"bytes","name":"encodedParams","type":"bytes"}],"internalType":"struct IVoucher.Voucher","name":"voucher","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"useVoucher","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint8","name":"action","type":"uint8"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint64","name":"expire","type":"uint64"},{"internalType":"uint32","name":"chainId","type":"uint32"},{"internalType":"bytes32","name":"voucherCodeHash","type":"bytes32"},{"internalType":"bytes","name":"encodedParams","type":"bytes"}],"internalType":"struct IVoucher.Voucher[]","name":"vouchers","type":"tuple[]"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"useVouchers","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60e0604052610b7c608081815260a091909152606b60c0526200002790600990600362000602565b50600a805462ff00001916623c000017905560408051608081018252610352815260786020820152601991810191909152600560608201526200006f90600b906004620006a3565b50604080516080810182526096815260646020820152603291810191909152600a6060820152620000a590600c90600462000710565b50604080518082019091526103bb8152602d6020820152620000cc90600d906002620006a3565b506040805160a081018252610190815261012c602082015260969181019190915260646060820152603260808201526200010b90600e906005620006a3565b503480156200011957600080fd5b5060405162004bea38038062004bea8339810160408190526200013c916200087b565b62000149600033620004a4565b620001757f339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab9533620004a4565b600f80546001600160a01b038086166001600160a01b031992831681179093556010805486831690841617905560118054918516919092161790556040805163313ce56760e01b815290516000929163313ce5679160048083019260209291908290030181865afa158015620001ef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002159190620008c5565b6200022290600a620009fd565b90506200023181603262000a0e565b6012556040805160808101909152806200024d83606462000a0e565b81526020016200025f8360c862000a0e565b815260200162000272836101f462000a0e565b815260200162000285836103e862000a0e565b9052620002979060139060046200077d565b50604080516101808101825260048152600560208201819052600a92820183905260196060830152601e6080830152600e60a083015260c0820192909252602460e08201526010610100820152600c6101208201819052610140820192909252601c6101608201526200030e9160069190620007ae565b50604080516101808101825260028152600360208201526007918101829052600660608201526009608082015260a08101829052600a60c0820152602460e08201526010610100820152600c61012082018190526005610140830152601c6101608301526200037f929190620007ae565b50604080516101008101825260108152600c60208201526005918101829052601c60608201526080810191909152600a60a0820152600860c08201819052600460e0830152620003d09181620007ae565b506200048360066000018054806020026020016040519081016040528092919081815260200182805480156200044457602002820191906000526020600020906000905b825461010083900a900460ff16815260206001928301818104948501949093039092029101808411620004145790505b50600993506000925062000456915050565b600891828204019190066004029054906101000a900463ffffffff166200054560201b62000e4d1760201c565b600a805461ffff191661ffff929092169190911790555062000a9b92505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1662000541576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620005003390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b81516000908190815b8160ff168160ff161015620005cd5760016200056b8683620005d9565b60018111156200057f576200057f62000a3e565b03620005b857858160ff16815181106200059d576200059d62000a28565b602002602001015160ff1683620005b5919062000a54565b92505b80620005c48162000a79565b9150506200054e565b50909150505b92915050565b6000600160ff83161b831663ffffffff1615620005f8576001620005fb565b60005b9392505050565b600183019183908215620006915791602002820160005b838211156200065d57835183826101000a81548163ffffffff021916908361ffff160217905550926020019260040160208160030104928301926001030262000619565b80156200068f5782816101000a81549063ffffffff02191690556004016020816003010492830192600103026200065d565b505b506200069f92915062000847565b5090565b8280548282559060005260206000209060070160089004810192821562000691579160200282016000838211156200065d57835183826101000a81548163ffffffff021916908361ffff160217905550926020019260040160208160030104928301926001030262000619565b82805482825590600052602060002090600701600890048101928215620006915791602002820160005b838211156200065d57835183826101000a81548163ffffffff021916908360ff16021790555092602001926004016020816003010492830192600103026200073a565b826004810192821562000691579160200282015b828111156200069157825182559160200191906001019062000791565b82805482825590600052602060002090601f01602090048101928215620006915791602002820160005b838211156200081857835183826101000a81548160ff021916908360ff1602179055509260200192600101602081600001049283019260010302620007d8565b80156200068f5782816101000a81549060ff021916905560010160208160000104928301926001030262000818565b5b808211156200069f576000815560010162000848565b80516001600160a01b03811681146200087657600080fd5b919050565b6000806000606084860312156200089157600080fd5b6200089c846200085e565b9250620008ac602085016200085e565b9150620008bc604085016200085e565b90509250925092565b600060208284031215620008d857600080fd5b815160ff81168114620005fb57600080fd5b634e487b7160e01b600052601160045260246000fd5b600181815b8085111562000941578160001904821115620009255762000925620008ea565b808516156200093357918102915b93841c939080029062000905565b509250929050565b6000826200095a57506001620005d3565b816200096957506000620005d3565b81600181146200098257600281146200098d57620009ad565b6001915050620005d3565b60ff841115620009a157620009a1620008ea565b50506001821b620005d3565b5060208310610133831016604e8410600b8410161715620009d2575081810a620005d3565b620009de838362000900565b8060001904821115620009f557620009f5620008ea565b029392505050565b6000620005fb60ff84168362000949565b8082028115828204841417620005d357620005d3620008ea565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b61ffff81811683821601908082111562000a725762000a72620008ea565b5092915050565b600060ff821660ff810362000a925762000a92620008ea565b60010192915050565b61413f8062000aab6000396000f3fe608060405234801561001057600080fd5b50600436106102115760003560e01c806399e27d7011610125578063d547741f116100ad578063ef32eb8f1161007c578063ef32eb8f14610480578063f4a0a52814610493578063f8742254146104a6578063faa3b976146104bb578063fea02d26146104ce57600080fd5b8063d547741f14610434578063d722848614610447578063dd34baa31461045a578063dd62a8b41461046d57600080fd5b8063a7f93ebd116100f4578063a7f93ebd146103eb578063b23f5c2f146103f3578063bbb2340d14610406578063bcb0e9df1461040e578063be28218c1461042157600080fd5b806399e27d70146103a35780639a3c1232146103b6578063a217fddf146103d0578063a59963fe146103d857600080fd5b806355cc4e57116101a85780636c118adb116101775780636c118adb146103415780637030e56714610354578063712817d61461036a57806391d148541461037d57806396eb26161461039057600080fd5b806355cc4e57146102fd5780635fe25eb814610310578063607c4ff9146103235780636817c76c1461033857600080fd5b8063248a9ca3116101e4578063248a9ca31461029f5780632c6cd32a146102c25780632f2ff15d146102d757806336568abe146102ea57600080fd5b806301ffc9a7146102165780631d1438481461023e5780631d3b3d0814610269578063240c650c1461028a575b600080fd5b6102296102243660046130fd565b6104e1565b60405190151581526020015b60405180910390f35b600454610251906001600160a01b031681565b6040516001600160a01b039091168152602001610235565b61027c610277366004613127565b610518565b604051908152602001610235565b61029261052f565b6040516102359190613140565b61027c6102ad366004613127565b60009081526020819052604090206001015490565b6102d56102d0366004613190565b61056a565b005b6102d56102e53660046131cd565b610594565b6102d56102f83660046131cd565b6105be565b6102d561030b3660046131fd565b610641565b6102d561031e36600461329f565b61066f565b61032b610735565b604051610235919061330a565b61027c60125481565b6102d561034f366004613127565b61079b565b61035c6107bc565b604051610235929190613338565b6102d56103783660046133cf565b61087e565b61022961038b3660046131cd565b6108bc565b601154610251906001600160a01b031681565b6102d56103b136600461349e565b6108e5565b6103be600581565b60405160ff9091168152602001610235565b61027c600081565b600f54610251906001600160a01b031681565b60125461027c565b6102d5610401366004613531565b61090a565b6103be603281565b6102d561041c366004613572565b610a81565b6102d561042f3660046133cf565b610b78565b6102d56104423660046131cd565b610b8d565b6102d561045536600461349e565b610bb2565b6102d561046836600461349e565b610bc7565b6102d561047b366004613594565b610ca3565b6102d561048e3660046133cf565b610cc3565b6102d56104a1366004613127565b610cef565b61027c6000805160206140ea83398151915281565b6102d56104c9366004613190565b610d9a565b601054610251906001600160a01b031681565b60006001600160e01b03198216637965db0b60e01b148061051257506301ffc9a760e01b6001600160e01b03198316145b92915050565b6013816004811061052857600080fd5b0154905081565b610537612fdf565b6040805160808101918290529060139060049082845b81548152602001906001019080831161054d575050505050905090565b600061057581610ed0565b50600a805460ff909216620100000262ff000019909216919091179055565b6000828152602081905260409020600101546105af81610ed0565b6105b98383610edd565b505050565b6001600160a01b03811633146106335760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b61063d8282610f61565b5050565b600061064c81610ed0565b50600480546001600160a01b0319166001600160a01b0392909216919091179055565b6106d6848460405160200161068592919061374a565b60408051601f198184030181526020601f860181900481028401810190925284835291908590859081908401838280828437600092019190915250506004546001600160a01b03169150610fc69050565b60005b60ff811684111561072e5761071c85858360ff168181106106fc576106fc6137bc565b905060200281019061070e91906137d2565b61071790613861565b611077565b806107268161392d565b9150506106d9565b5050505050565b61073d612ffd565b60408051606081019182905290600990600390826000855b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116107555790505050505050905090565b6000805160206140ea8339815191526107b381610ed0565b61063d82600255565b6107c461301b565b600a5460408051606081019091526000916006916201000090910460ff169082600385835b828210156108705783820180548060200260200160405190810160405280929190818152602001828054801561085c57602002820191906000526020600020906000905b825461010083900a900460ff1681526020600192830181810494850194909303909202910180841161082d5790505b5050505050815260200190600101906107e9565b505050509150915091509091565b600061088981610ed0565b81600960025b600891828204019190066004026101000a81548163ffffffff021916908363ffffffff1602179055505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006108f081610ed0565b81600660025b0190805190602001906105b9929190613042565b6010546000906013906109a4906001906001600160a01b03166365d6759a87878781610938576109386137bc565b905060200201356040518263ffffffff1660e01b815260040161095d91815260200190565b602060405180830381865afa15801561097a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099e919061394c565b90611275565b60ff16600481106109b7576109b76137bc565b0154600f546011546040516323b872dd60e01b81523360048201526001600160a01b0391821660248201526044810184905292935016906323b872dd906064016020604051808303816000875af1158015610a16573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3a9190613975565b50610a7b33848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250600192506112a2915050565b50505050565b6000805160206140ea833981519152610a9981610ed0565b60005b600460ff821610156105b957600f60009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610afb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1f9190613990565b610b2a90600a613a91565b838260ff1660048110610b3f57610b3f6137bc565b6020020135610b4e9190613aa0565b60138260ff1660048110610b6457610b646137bc565b015580610b708161392d565b915050610a9c565b6000610b8381610ed0565b816009600161088f565b600082815260208190526040902060010154610ba881610ed0565b6105b98383610f61565b6000610bbd81610ed0565b81600660016108f6565b6000610bd281610ed0565b8151610be5906006906020850190613042565b50610c89600660005b01805480602002602001604051908101604052809291908181526020018280548015610c5757602002820191906000526020600020906000905b825461010083900a900460ff16815260206001928301818104948501949093039092029101808411610c285790505b506009935060009250610c68915050565b600891828204019190066004029054906101000a900463ffffffff16610e4d565b600a805461ffff191661ffff929092169190911790555050565b610cb7836040516020016106859190613ab7565b6105b961071784613861565b6000610cce81610ed0565b6009805463ffffffff191663ffffffff8416179055610c8960066000610bee565b6000805160206140ea833981519152610d0781610ed0565b600f60009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e9190613990565b610d8990600a613a91565b610d939083613aa0565b6012555050565b600f546011546012546001600160a01b03928316926323b872dd923392911690610dc89060ff871690613aa0565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303816000875af1158015610e1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e409190613975565b5061063d33826001611526565b81516000908190815b8160ff168160ff161015610ec6576001610e7086836116cd565b6001811115610e8157610e81613aca565b03610eb457858160ff1681518110610e9b57610e9b6137bc565b602002602001015160ff1683610eb19190613ae0565b92505b80610ebe8161392d565b915050610e56565b5090949350505050565b610eda81336116f3565b50565b610ee782826108bc565b61063d576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610f1d3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610f6b82826108bc565b1561063d576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006110308361102a86805190602001206040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b9061174c565b9050816001600160a01b0316816001600160a01b031614610a7b5760405163064eac6f60e41b81526001600160a01b0380841660048301528216602482015260440161062a565b61108081611770565b60c08101516000908152600560205260408120805460ff1916600117905560ff16816020015160ff160361115d5760008160e001518060200190518101906110c89190613b02565b80519091506001600160a01b031615806110e75750602081015160ff16155b806110fc5750603260ff16816020015160ff16115b1561113f5760408051637309cf3160e11b815282516001600160a01b03166004820152602083015160ff166024820152908201511515604482015260640161062a565b611156816000015182602001518360400151611526565b50506111fb565b600160ff16816020015160ff16036111e05760008160e001518060200190518101906111899190613bba565b80519091506001600160a01b031615806111a95750602081015151600514155b156111c95780604051639474718560e01b815260040161062a9190613c85565b6111568160000151826020015183604001516112a2565b80604051635491e70f60e11b815260040161062a9190613d1b565b7f7697a9d8fa2cf7dd6cb14a34b76d2e6577843156db8afa185d9181e28f607ba9816040015182602001518360c001518460a0015160405161126a94939291906001600160a01b0394909416845260ff929092166020840152604083015263ffffffff16606082015260800190565b60405180910390a150565b600080611283600884613dc2565b905060ff811684901c6112996001610100613dde565b16949350505050565b81516000906005146112c957826040516318ef34ff60e01b815260040161062a9190613df1565b601054604051631a1111a560e31b81526001600160a01b039091169063d0888d28906112fb9087908790600401613e04565b602060405180830381865afa158015611318573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133c9190613975565b61135b57826040516318ef34ff60e01b815260040161062a9190613df1565b6010546040516307e02e9360e01b81526000916001600160a01b0316906307e02e939061138c908790600401613df1565b600060405180830381865afa1580156113a9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113d19190810190613e28565b90506113dc8161181f565b60105460405163e4623c1b60e01b81526001600160a01b039091169063e4623c1b9061140c908790600401613df1565b600060405180830381600087803b15801561142657600080fd5b505af115801561143a573d6000803e3d6000fd5b50505050600061145e600185611457856114526119f4565b611a79565b9190611f15565b6010546040516308934a5f60e31b81526001600160a01b038981166004830152602482018490529293506000929091169063449a52f8906044016020604051808303816000875af11580156114b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114db919061394c565b90507f73ca68268ae2c371e10eee845d2bf56ec6ef514fc3eb92b7691d0794db6c8ecb878783466040516115129493929190613e5c565b60405180910390a1925050505b9392505050565b606060ff8316158061153b5750603260ff8416115b156115655760405163456a182760e11b81526000600482015260ff8416602482015260440161062a565b8260ff166001600160401b03811115611580576115806133ea565b6040519080825280602002602001820160405280156115a9578160200160208202803683370190505b50905060008360ff166001600160401b038111156115c9576115c96133ea565b6040519080825280602002602001820160405280156115f2578160200160208202803683370190505b50905060005b8460ff1681101561164a5761161b60018561145760006116166119f4565b611f42565b82828151811061162d5761162d6137bc565b60209081029190910101528061164281613e93565b9150506115f8565b5060105460405163e5b0c23760e01b81526001600160a01b039091169063e5b0c2379061167d9088908590600401613e04565b6000604051808303816000875af115801561169c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526116c49190810190613e28565b95945050505050565b6000600160ff83161b831663ffffffff16156116ea57600161151f565b60009392505050565b6116fd82826108bc565b61063d5761170a81612106565b611715836020612118565b604051602001611726929190613eac565b60408051601f198184030181529082905262461bcd60e51b825261062a91600401613f21565b600080600061175b85856122b3565b91509150611768816122f8565b509392505050565b60c081015160009081526005602052604090205460ff16156117ad578060c00151604051631a328db560e01b815260040161062a91815260200190565b80516001600160a01b0316301415806117d3575060408101516001600160a01b03163314155b806117ea575080608001516001600160401b031642115b806117ff5750468160a0015163ffffffff1614155b15610eda5780604051635491e70f60e11b815260040161062a9190613d1b565b61182a816000612442565b158061183e575061183c816001612442565b155b8061187c5750600260ff16611877600083600081518110611861576118616137bc565b602002602001015161127590919063ffffffff16565b60ff16145b1561189c578060405163118c2c6960e21b815260040161062a9190613df1565b60006118b6600183600081518110611861576118616137bc565b60ff1660038111156118ca576118ca613aca565b905060006118d9836002612442565b905060006118e8846003612442565b905060006118f78560036124ce565b9050600084600381111561190d5761190d613aca565b03611943578215801561191e575081155b1561193e578460405163118c2c6960e21b815260040161062a9190613df1565b61072e565b600184600381111561195757611957613aca565b14806119745750600284600381111561197257611972613aca565b145b156119a15782158061191e57508161193e578460405163118c2c6960e21b815260040161062a9190613df1565b600160ff166119be600087600081518110611861576118616137bc565b60ff1614806119cb575082155b806119d4575080155b1561072e578460405163118c2c6960e21b815260040161062a9190613df1565b600154604080516020810192909252339082015242606082015260009060800160408051808303601f19018152828252805160209182012060018181556003805490910190819055600254928501919091529183015260608201523360808201524260a082015260c00160405160208183030381529060405280519060200120905090565b600080611a94600085600081518110611861576118616137bc565b90506000611ab0600186600081518110611861576118616137bc565b60ff166003811115611ac457611ac4613aca565b905083601881901b60e882901c1760ff8416611be257611b6183600c805480602002602001604051908101604052809291908181526020018280548015611b5657602002820191906000526020600020906000905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611b195790505b505050505084612571565b15611ba0576000611b73600183611f42565b9050611b946001856003811115611b8c57611b8c613aca565b83919061268a565b95505050505050610512565b6003836003811115611bb457611bb4613aca565b03611be257600a54611bd790889061ffff81169062010000900460ff16846126dd565b945050505050610512565b6000611bef81808761268a565b9050611c196001611c0e60018b600081518110611861576118616137bc565b611b8c906001613f34565b9050600060068660ff1660038110611c3357611c336137bc565b01805480602002602001604051908101604052809291908181526020018280548015611c9c57602002820191906000526020600020906000905b825461010083900a900460ff16815260206001928301818104948501949093039092029101808411611c6d5790505b50505050509050600060098760ff1660038110611cbb57611cbb6137bc565b600891828204019190066004029054906101000a900463ffffffff16905060005b82518160ff161015611e5d57939450601885901b60e886901c17936000611e2f8c611d08846002613f34565b868560ff1681518110611d1d57611d1d6137bc565b6020026020010151611d2f87876116cd565b600d805480602002602001604051908101604052809291908181526020018280548015611da757602002820191906000526020600020906000905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611d6a5790505b5050505050600e805480602002602001604051908101604052809291908181526020018280548015611e2457602002820191906000526020600020906000905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611de75790505b50505050508d6128a8565b9050611e47611e3f836002613f34565b86908361268a565b9450508080611e559061392d565b915050611cdc565b5060ff8716611efa57929350601884901b60e885901c17926000866003811115611e8957611e89613aca565b03611ece57611ec783600484600260045b611ea49190613f4d565b60ff1681518110611eb757611eb76137bc565b6020026020010151600189612947565b9250611efa565b6001866003811115611ee257611ee2613aca565b03611efa57611ef78360058460026005611e9a565b92505b611f0783601f60d161268a565b9a9950505050505050505050565b600080611f2385601e611275565b90508215611f32578317611f36565b8319165b6116c485601e8361268a565b600060ff831615801590611f5a575060ff8316600114155b15611f845760405163456a182760e11b815260ff841660048201526001602482015260440161062a565b81601881901b60e882901c176000611f9d81808861268a565b905061202a6001611b8c600b80548060200260200160405190810160405280929190818152602001828054801561201f57602002820191906000526020600020906000905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411611fe25790505b50505050508661299a565b90506120ed818760068960ff1660038110612047576120476137bc565b018054806020026020016040519081016040528092919081815260200182805480156120b057602002820191906000526020600020906000905b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116120815790505b505050505060098a60ff16600381106120cb576120cb6137bc565b600891828204019190066004029054906101000a900463ffffffff1686612a62565b90506120fc81601f60d161268a565b9695505050505050565b60606105126001600160a01b03831660145b60606000612127836002613aa0565b612132906002613f66565b6001600160401b03811115612149576121496133ea565b6040519080825280601f01601f191660200182016040528015612173576020820181803683370190505b509050600360fc1b8160008151811061218e5761218e6137bc565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106121bd576121bd6137bc565b60200101906001600160f81b031916908160001a90535060006121e1846002613aa0565b6121ec906001613f66565b90505b6001811115612264576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110612220576122206137bc565b1a60f81b828281518110612236576122366137bc565b60200101906001600160f81b031916908160001a90535060049490941c9361225d81613f79565b90506121ef565b50831561151f5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161062a565b60008082516041036122e95760208301516040840151606085015160001a6122dd87828585612b79565b945094505050506122f1565b506000905060025b9250929050565b600081600481111561230c5761230c613aca565b036123145750565b600181600481111561232857612328613aca565b036123755760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161062a565b600281600481111561238957612389613aca565b036123d65760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161062a565b60038160048111156123ea576123ea613aca565b03610eda5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161062a565b6000806124698460008151811061245b5761245b6137bc565b602002602001015184611275565b905060015b84518110156124c3578160ff1661249e868381518110612490576124906137bc565b602002602001015186611275565b60ff16146124b157600092505050610512565b806124bb81613e93565b91505061246e565b506001949350505050565b6000806124e78460008151811061245b5761245b6137bc565b60ff166001901b90506000600190505b84518110156124c357612523858281518110612515576125156137bc565b602002602001015185611275565b60ff166001901b821660001461253e57600092505050610512565b612553858281518110612515576125156137bc565b60ff166001901b82179150808061256990613e93565b9150506124f7565b600083600381111561258557612585613aca565b60ff168351116125975750600061151f565b6000838560038111156125ac576125ac613aca565b60ff16815181106125bf576125bf6137bc565b60200260200101519050600060026001600160401b038111156125e4576125e46133ea565b60405190808252806020026020018201604052801561260d578160200160208202803683370190505b5090508181600081518110612624576126246137bc565b63ffffffff90921660209283029190910190910152612645826103e8613f90565b81600181518110612658576126586137bc565b602002602001019063ffffffff16908163ffffffff168152505061267c818561299a565b60ff16159695505050505050565b600080612698600885613dc2565b905060006126ad600160ff841681901b613dde565b60ff83811688901c60ff1916979190911691506126cc90851687613f66565b60ff83161b95506120fc8187613f66565b600081601881901b60e882901c1782805b88518160ff16101561274c5761272e898260ff1681518110612712576127126137bc565b602090810291909101015160065460095463ffffffff16612c3d565b6127389083613ae0565b9150806127448161392d565b9150506126ee565b50600088518861275c9190613fad565b9050600061276b600189613fd3565b905060008261ffff168261ffff168561ffff166127889190613fee565b6127929190614027565b90506000806127a383856005612cb2565b9150915060006127b7898361ffff16612d36565b6127c19084613ae0565b905060006127d18180600261268a565b90506127df8160018461268a565b90506128868160026006810180548060200260200160405190810160405280929190818152602001828054801561285357602002820191906000526020600020906000905b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116128245790505b506009935060029250612864915050565b600891828204019190066004029054906101000a900463ffffffff168d612a62565b905061289581601f60d261268a565b9f9e505050505050505050505050505050565b600060018560018111156128be576128be613aca565b1480156128d757506128d0848361299a565b60ff166001145b156129115760006128e88989612d46565b90508660ff168160ff161461290757612902816001613f34565b612909565b865b91505061293c565b600061291d848461299a565b9050612938888a8360ff1681518110611861576118616137bc565b9150505b979650505050505050565b6000808084600181111561295d5761295d613aca565b036129765761296f838660ff16612d36565b9050612983565b6129808584612d92565b90505b61293c86612992836001613f34565b89919061268a565b6000806129a684612dfb565b90508351600014806129bb575062ffffff8116155b156129db57836040516322f0871560e11b815260040161062a919061404d565b60006129e78483612d36565b62ffffff1690506000805b86518160ff161015612a5357868160ff1681518110612a1357612a136137bc565b602002602001015163ffffffff1682612a2c9190613f66565b915081831015612a4157935061051292505050565b80612a4b8161392d565b9150506129f2565b50600186516120fc9190613dde565b825160009081805b8260ff168160ff161015612ade576000612a8487836116cd565b601887901b60e888901c179350905085612ac78b612aa3856002613f34565b8b8660ff1681518110612ab857612ab86137bc565b60200260200101518585612947565b9a5050508080612ad69061392d565b915050612a6a565b5060ff8716612b6d576000612af4896001611275565b60ff166003811115612b0857612b08613aca565b90506000816003811115612b1e57612b1e613aca565b03612b4557612b3260045b8a90600061268a565b9850612b3e6005612b29565b9850612b6b565b6001816003811115612b5957612b59613aca565b03612b6b57612b686005612b29565b98505b505b50959695505050505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115612bb05750600090506003612c34565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015612c04573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116612c2d57600060019250925050612c34565b9150600090505b94509492505050565b600080805b8460ff168160ff161015612ca9576001612c5c85836116cd565b6001811115612c6d57612c6d613aca565b03612c9757612c87612c80600283613f34565b8790611275565b612c949060ff1683613ae0565b91505b80612ca18161392d565b915050612c42565b50949350505050565b6000808261ffff168561ffff161015612cda5760009150612cd38386613ae0565b9050612d2e565b612ce48386613ae0565b61ffff168461ffff161015612d1457612cfd8386613fd3565b9150612d098285613fd3565b612cd3906001613ae0565b612d1e8386613fd3565b9150612d2b836002613fad565b90505b935093915050565b600061151f8260e885901c614097565b600080805b8451811015611768576000612d6b868381518110612490576124906137bc565b90508060ff168360ff161015612d7f578092505b5080612d8a81613e93565b915050612d4b565b600060ff831681612da684620f4240612d36565b612db19060016140b9565b62ffffff169050600082612dc483612e4d565b612dd0906103e8613f66565b612ddb856002613aa0565b612de7906103e8613aa0565b612df191906140d5565b6120fc9190613dde565b6000805b8251811015612e4757828181518110612e1a57612e1a6137bc565b602002602001015163ffffffff1682612e339190613f66565b915080612e3f81613e93565b915050612dff565b50919050565b600081600003612e5f57506000919050565b60006001612e6c84612f35565b901c6001901b90506001818481612e8557612e85614011565b048201901c90506001818481612e9d57612e9d614011565b048201901c90506001818481612eb557612eb5614011565b048201901c90506001818481612ecd57612ecd614011565b048201901c90506001818481612ee557612ee5614011565b048201901c90506001818481612efd57612efd614011565b048201901c90506001818481612f1557612f15614011565b048201901c905061151f81828581612f2f57612f2f614011565b04612fc9565b600080608083901c15612f4a57608092831c92015b604083901c15612f5c57604092831c92015b602083901c15612f6e57602092831c92015b601083901c15612f8057601092831c92015b600883901c15612f9257600892831c92015b600483901c15612fa457600492831c92015b600283901c15612fb657600292831c92015b600183901c156105125760010192915050565b6000818310612fd8578161151f565b5090919050565b60405180608001604052806004906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b60405180606001604052806003905b606081526020019060019003908161302a5790505090565b82805482825590600052602060002090601f016020900481019282156130d85791602002820160005b838211156130a957835183826101000a81548160ff021916908360ff160217905550926020019260010160208160000104928301926001030261306b565b80156130d65782816101000a81549060ff02191690556001016020816000010492830192600103026130a9565b505b506130e49291506130e8565b5090565b5b808211156130e457600081556001016130e9565b60006020828403121561310f57600080fd5b81356001600160e01b03198116811461151f57600080fd5b60006020828403121561313957600080fd5b5035919050565b60808101818360005b6004811015613168578151835260209283019290910190600101613149565b50505092915050565b60ff81168114610eda57600080fd5b803561318b81613171565b919050565b6000602082840312156131a257600080fd5b813561151f81613171565b6001600160a01b0381168114610eda57600080fd5b803561318b816131ad565b600080604083850312156131e057600080fd5b8235915060208301356131f2816131ad565b809150509250929050565b60006020828403121561320f57600080fd5b813561151f816131ad565b60008083601f84011261322c57600080fd5b5081356001600160401b0381111561324357600080fd5b6020830191508360208260051b85010111156122f157600080fd5b60008083601f84011261327057600080fd5b5081356001600160401b0381111561328757600080fd5b6020830191508360208285010111156122f157600080fd5b600080600080604085870312156132b557600080fd5b84356001600160401b03808211156132cc57600080fd5b6132d88883890161321a565b909650945060208701359150808211156132f157600080fd5b506132fe8782880161325e565b95989497509550505050565b60608101818360005b600381101561316857815163ffffffff16835260209283019290910190600101613313565b604080825260009060a083019083018583805b60038110156133a757868503603f19018452825180518087526020918201918088019190855b8281101561339057845160ff1684529381019392810192600101613371565b50919750509485019493909301925060010161334b565b50505060ff8516602085015250905061151f565b803563ffffffff8116811461318b57600080fd5b6000602082840312156133e157600080fd5b61151f826133bb565b634e487b7160e01b600052604160045260246000fd5b60405161010081016001600160401b0381118282101715613423576134236133ea565b60405290565b604051606081016001600160401b0381118282101715613423576134236133ea565b604051601f8201601f191681016001600160401b0381118282101715613473576134736133ea565b604052919050565b60006001600160401b03821115613494576134946133ea565b5060051b60200190565b600060208083850312156134b157600080fd5b82356001600160401b038111156134c757600080fd5b8301601f810185136134d857600080fd5b80356134eb6134e68261347b565b61344b565b81815260059190911b8201830190838101908783111561350a57600080fd5b928401925b8284101561293c57833561352281613171565b8252928401929084019061350f565b6000806020838503121561354457600080fd5b82356001600160401b0381111561355a57600080fd5b6135668582860161321a565b90969095509350505050565b60006080828403121561358457600080fd5b82608083011115612e4757600080fd5b6000806000604084860312156135a957600080fd5b83356001600160401b03808211156135c057600080fd5b9085019061010082880312156135d557600080fd5b909350602085013590808211156135eb57600080fd5b506135f88682870161325e565b9497909650939450505050565b80356001600160401b038116811461318b57600080fd5b6000808335601e1984360301811261363357600080fd5b83016020810192503590506001600160401b0381111561365257600080fd5b8036038213156122f157600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000610100823561369a816131ad565b6001600160a01b0316845260208301356136b381613171565b60ff1660208501526136c7604084016131c2565b6001600160a01b031660408501526136e1606084016131c2565b6001600160a01b031660608501526136fb60808401613605565b6001600160401b0316608085015261371560a084016133bb565b63ffffffff1660a085015260c0838101359085015261373760e084018461361c565b8260e08701526120fc8387018284613661565b60208082528181018390526000906040600585901b840181019084018684805b888110156137ae57878503603f190184528235368b900360fe1901811261378f578283fd5b61379b868c830161368a565b955050928501929185019160010161376a565b509298975050505050505050565b634e487b7160e01b600052603260045260246000fd5b6000823560fe198336030181126137e857600080fd5b9190910192915050565b600082601f83011261380357600080fd5b81356001600160401b0381111561381c5761381c6133ea565b61382f601f8201601f191660200161344b565b81815284602083860101111561384457600080fd5b816020850160208301376000918101602001919091529392505050565b6000610100823603121561387457600080fd5b61387c613400565b613885836131c2565b815261389360208401613180565b60208201526138a4604084016131c2565b60408201526138b5606084016131c2565b60608201526138c660808401613605565b60808201526138d760a084016133bb565b60a082015260c083013560c082015260e08301356001600160401b038111156138ff57600080fd5b61390b368286016137f2565b60e08301525092915050565b634e487b7160e01b600052601160045260246000fd5b600060ff821660ff810361394357613943613917565b60010192915050565b60006020828403121561395e57600080fd5b5051919050565b8051801515811461318b57600080fd5b60006020828403121561398757600080fd5b61151f82613965565b6000602082840312156139a257600080fd5b815161151f81613171565b600181815b808511156139e85781600019048211156139ce576139ce613917565b808516156139db57918102915b93841c93908002906139b2565b509250929050565b6000826139ff57506001610512565b81613a0c57506000610512565b8160018114613a225760028114613a2c57613a48565b6001915050610512565b60ff841115613a3d57613a3d613917565b50506001821b610512565b5060208310610133831016604e8410600b8410161715613a6b575081810a610512565b613a7583836139ad565b8060001904821115613a8957613a89613917565b029392505050565b600061151f60ff8416836139f0565b808202811582820484141761051257610512613917565b60208152600061151f602083018461368a565b634e487b7160e01b600052602160045260246000fd5b61ffff818116838216019080821115613afb57613afb613917565b5092915050565b600060608284031215613b1457600080fd5b613b1c613429565b8251613b27816131ad565b81526020830151613b3781613171565b6020820152613b4860408401613965565b60408201529392505050565b600082601f830112613b6557600080fd5b81516020613b756134e68361347b565b82815260059290921b84018101918181019086841115613b9457600080fd5b8286015b84811015613baf5780518352918301918301613b98565b509695505050505050565b600060208284031215613bcc57600080fd5b81516001600160401b0380821115613be357600080fd5b9083019060608286031215613bf757600080fd5b613bff613429565b8251613c0a816131ad565b8152602083015182811115613c1e57600080fd5b613c2a87828601613b54565b602083015250613c3c60408401613965565b604082015295945050505050565b600081518084526020808501945080840160005b83811015613c7a57815187529582019590820190600101613c5e565b509495945050505050565b602080825282516001600160a01b03168282015282015160606040830152600090613cb36080840182613c4a565b90506040840151151560608401528091505092915050565b60005b83811015613ce6578181015183820152602001613cce565b50506000910152565b60008151808452613d07816020860160208601613ccb565b601f01601f19169290920160200192915050565b6020815260018060a01b03825116602082015260ff602083015116604082015260006040830151613d5760608401826001600160a01b03169052565b5060608301516001600160a01b03811660808401525060808301516001600160401b03811660a08401525060a083015163ffffffff811660c08401525060c083015160e083015260e0830151610100808185015250613dba610120840182613cef565b949350505050565b60ff8181168382160290811690818114613afb57613afb613917565b8181038181111561051257610512613917565b60208152600061151f6020830184613c4a565b6001600160a01b0383168152604060208201819052600090613dba90830184613c4a565b600060208284031215613e3a57600080fd5b81516001600160401b03811115613e5057600080fd5b613dba84828501613b54565b6001600160a01b0385168152608060208201819052600090613e8090830186613c4a565b6040830194909452506060015292915050565b600060018201613ea557613ea5613917565b5060010190565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351613ee4816017850160208801613ccb565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351613f15816028840160208801613ccb565b01602801949350505050565b60208152600061151f6020830184613cef565b60ff818116838216019081111561051257610512613917565b60ff828116828216039081111561051257610512613917565b8082018082111561051257610512613917565b600081613f8857613f88613917565b506000190190565b63ffffffff828116828216039080821115613afb57613afb613917565b61ffff818116838216028082169190828114613fcb57613fcb613917565b505092915050565b61ffff828116828216039080821115613afb57613afb613917565b6001600160401b03818116838216028082169190828114613fcb57613fcb613917565b634e487b7160e01b600052601260045260246000fd5b60006001600160401b038084168061404157614041614011565b92169190910492915050565b6020808252825182820181905260009190848201906040850190845b8181101561408b57835163ffffffff1683529284019291840191600101614069565b50909695505050505050565b600062ffffff808416806140ad576140ad614011565b92169190910692915050565b62ffffff818116838216019080821115613afb57613afb613917565b6000826140e4576140e4614011565b50049056fe339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab95a264697066735822122043ca8fc5ff38a76520af10bd6f1fb427e128b36bf39dacdf12a96a2426323e9f64736f6c6343000812003300000000000000000000000018e73a5333984549484348a94f4d219f4fab7b81000000000000000000000000435b74f6dc4a0723ca19e4dd2ac8aa1361c7b0f000000000000000000000000068d1e3f802058ce517e9ba871ab182299e74d852

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

00000000000000000000000018e73a5333984549484348a94f4d219f4fab7b81000000000000000000000000435b74f6dc4a0723ca19e4dd2ac8aa1361c7b0f000000000000000000000000068d1e3f802058ce517e9ba871ab182299e74d852

-----Decoded View---------------
Arg [0] : duckiesAddress (address): 0x18e73a5333984549484348a94f4d219f4fab7b81
Arg [1] : ducklingsAddress (address): 0x435b74f6dc4a0723ca19e4dd2ac8aa1361c7b0f0
Arg [2] : treasureVaultAddress_ (address): 0x68d1e3f802058ce517e9ba871ab182299e74d852

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 00000000000000000000000018e73a5333984549484348a94f4d219f4fab7b81
Arg [1] : 000000000000000000000000435b74f6dc4a0723ca19e4dd2ac8aa1361c7b0f0
Arg [2] : 00000000000000000000000068d1e3f802058ce517e9ba871ab182299e74d852


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.