Contract Overview
My Name Tag:
Not Available, login to update
[ Download CSV Export ]
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
Contract Source Code (Solidity Standard Json-Input format)
// 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); }
// 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); }
// 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()); } } }
// 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; }
// 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 {} }
// 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); } }
// 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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts 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; } }
// 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)); } }
// 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; } }
// 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); }
// 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); } } }
// 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); } }
// 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; } }
// 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; } } }
// 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; } }
// 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)); } }
// 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); } }
// 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; }
// 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; }
// 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); }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
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
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.