ETH Price: $2,285.12 (+5.03%)

Transaction Decoder

Block:
19162530 at Feb-05-2024 02:01:11 PM +UTC
Transaction Fee:
0.003316438150169 ETH $7.58
Gas Used:
107,000 Gas / 30.994749067 Gwei

Emitted Events:

Account State Difference:

  Address   Before After State Difference Code
0x28c0647d...48b74c792
0.035027285760962264 Eth
Nonce: 194
0.031710847610793264 Eth
Nonce: 195
0.003316438150169
(Boba Builder)
0.095378572431400915 Eth0.095381274107998915 Eth0.000002701676598
0xBE6889e5...be878cc93

Execution Trace

CitizenZero.CALL( )
  • CitizenZero.STATICCALL( )
    //SPDX-License-Identifier: GPL 3
    pragma solidity >=0.8.0 <0.9.0;
    
    library Counters {
        struct Counter {
            uint256 _value;
        }
    
        function current(Counter storage counter) internal view returns (uint256) {
            return counter._value;
        }
    
        function increment(Counter storage counter) internal {
            unchecked {
                counter._value += 1;
            }
        }
    
        function decrement(Counter storage counter) internal {
            uint256 value = counter._value;
            require(value > 0, "Counter: decrement overflow");
            unchecked {
                counter._value = value - 1;
            }
        }
    
        function reset(Counter storage counter) internal {
            counter._value = 0;
        }
    }
    
    contract TokenAccessControl {
        bool public paused = false;
        address public owner;
        address public newContractOwner;
        mapping(address => bool) public authorizedContracts;
    
        event Pause();
        event OwnershipTransferred(
            address indexed previousOwner,
            address indexed newOwner
        );
    
        constructor() {
            owner = msg.sender;
        }
    
        modifier ifNotPaused() {
            require(!paused, "contract is paused");
            _;
        }
    
        modifier onlyOwner() {
            require(msg.sender == owner, "caller is not an owner");
            _;
        }
    
        modifier onlyAuthorizedUser() {
            require(
                authorizedContracts[msg.sender],
                "caller is not an authorized user"
            );
            _;
        }
    
        modifier onlyOwnerOrAuthorizedUser() {
            require(
                authorizedContracts[msg.sender] || msg.sender == owner,
                "caller is not an authorized user or an owner"
            );
            _;
        }
    
        function renounceOwnership() public virtual onlyOwner {
            emit OwnershipTransferred(owner, address(0));
            owner = address(0);
        }
    
        function transferOwnership(address _newOwner) public onlyOwner {
            require(_newOwner != address(0));
            newContractOwner = _newOwner;
        }
    
        function acceptOwnership() public ifNotPaused {
            require(msg.sender == newContractOwner);
            emit OwnershipTransferred(owner, newContractOwner);
            owner = newContractOwner;
            newContractOwner = address(0);
        }
    
        function setAuthorizedUser(address _operator, bool _approve)
            public
            onlyOwner
        {
            if (_approve) {
                authorizedContracts[_operator] = true;
            } else {
                delete authorizedContracts[_operator];
            }
        }
    
        function setPause(bool _paused) public onlyOwner {
            paused = _paused;
            if (paused) {
                emit Pause();
            }
        }
    }
    
    interface IERC20 {
        event Transfer(address indexed from, address indexed to, uint256 value);
        event Approval(
            address indexed owner,
            address indexed spender,
            uint256 value
        );
    
        function totalSupply() external view returns (uint256);
    
        function balanceOf(address account) external view returns (uint256);
    
        function transfer(address to, uint256 amount) external returns (bool);
    
        function allowance(address owner, address spender)
            external
            view
            returns (uint256);
    
        function approve(address spender, uint256 amount) external returns (bool);
    
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) external returns (bool);
    }
    
    library SignedMath {
        /**
         * @dev Returns the largest of two signed numbers.
         */
        function max(int256 a, int256 b) internal pure returns (int256) {
            return a > b ? a : b;
        }
    
        /**
         * @dev Returns the smallest of two signed numbers.
         */
        function min(int256 a, int256 b) internal pure returns (int256) {
            return a < b ? a : b;
        }
    
        /**
         * @dev Returns the average of two signed numbers without overflow.
         * The result is rounded towards zero.
         */
        function average(int256 a, int256 b) internal pure returns (int256) {
            // Formula from the book "Hacker's Delight"
            int256 x = (a & b) + ((a ^ b) >> 1);
            return x + (int256(uint256(x) >> 255) & (a ^ b));
        }
    
        /**
         * @dev Returns the absolute unsigned value of a signed value.
         */
        function abs(int256 n) internal pure returns (uint256) {
            unchecked {
                // must be unchecked in order to support `n = type(int256).min`
                return uint256(n >= 0 ? n : -n);
            }
        }
    }
    
    library Math {
        enum Rounding {
            Down, // Toward negative infinity
            Up, // Toward infinity
            Zero // Toward zero
        }
    
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         *
         * _Available since v5.0._
         */
        function tryAdd(uint256 a, uint256 b)
            internal
            pure
            returns (bool, uint256)
        {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
    
        /**
         * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
         *
         * _Available since v5.0._
         */
        function trySub(uint256 a, uint256 b)
            internal
            pure
            returns (bool, uint256)
        {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
    
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         *
         * _Available since v5.0._
         */
        function tryMul(uint256 a, uint256 b)
            internal
            pure
            returns (bool, uint256)
        {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
    
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         *
         * _Available since v5.0._
         */
        function tryDiv(uint256 a, uint256 b)
            internal
            pure
            returns (bool, uint256)
        {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         *
         * _Available since v5.0._
         */
        function tryMod(uint256 a, uint256 b)
            internal
            pure
            returns (bool, uint256)
        {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
    
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
    
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
    
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
    
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds 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) {
                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                    // The surrounding unchecked block does not change this fact.
                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                    return prod0 / denominator;
                }
    
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                require(denominator > prod1, "Math: mulDiv overflow");
    
                ///////////////////////////////////////////////
                // 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 256, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log256(uint256 value, Rounding rounding)
            internal
            pure
            returns (uint256)
        {
            unchecked {
                uint256 result = log256(value);
                return
                    result +
                    (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
            }
        }
    }
    
    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 `int256` to its ASCII `string` decimal representation.
         */
        function toString(int256 value) internal pure returns (string memory) {
            return
                string(
                    abi.encodePacked(
                        value < 0 ? "-" : "",
                        toString(SignedMath.abs(value))
                    )
                );
        }
    
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            unchecked {
                return toHexString(value, Math.log256(value) + 1);
            }
        }
    
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length)
            internal
            pure
            returns (string memory)
        {
            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);
        }
    
        /**
         * @dev Returns true if the two strings are equal.
         */
        function equal(string memory a, string memory b)
            internal
            pure
            returns (bool)
        {
            return keccak256(bytes(a)) == keccak256(bytes(b));
        }
    }
    
    interface IERC721Receiver {
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    
    interface IERC165 {
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    
    interface IERC2981 is IERC165 {
        function royaltyInfo(uint256 tokenId, uint256 salePrice)
            external
            view
            returns (address receiver, uint256 royaltyAmount);
    }
    
    abstract contract ERC165 is IERC165 {
        function supportsInterface(bytes4 interfaceId)
            public
            view
            virtual
            override
            returns (bool)
        {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    
    abstract contract ERC2981 is IERC2981, ERC165 {
        struct RoyaltyInfo {
            address receiver;
            uint96 royaltyFraction;
        }
    
        RoyaltyInfo private _defaultRoyaltyInfo;
        mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
    
        function supportsInterface(bytes4 interfaceId)
            public
            view
            virtual
            override(IERC165, ERC165)
            returns (bool)
        {
            return
                interfaceId == type(IERC2981).interfaceId ||
                super.supportsInterface(interfaceId);
        }
    
        function royaltyInfo(uint256 tokenId, uint256 salePrice)
            public
            view
            virtual
            override
            returns (address, uint256)
        {
            RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];
    
            if (royalty.receiver == address(0)) {
                royalty = _defaultRoyaltyInfo;
            }
    
            uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) /
                _feeDenominator();
    
            return (royalty.receiver, royaltyAmount);
        }
    
        function _feeDenominator() internal pure virtual returns (uint96) {
            return 10000;
        }
    
        function _setDefaultRoyalty(address receiver, uint96 feeNumerator)
            internal
            virtual
        {
            require(
                feeNumerator <= _feeDenominator(),
                "ERC2981: royalty fee will exceed salePrice"
            );
            require(receiver != address(0), "ERC2981: invalid receiver");
    
            _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
        }
    
        function _deleteDefaultRoyalty() internal virtual {
            delete _defaultRoyaltyInfo;
        }
    
        function _setTokenRoyalty(
            uint256 tokenId,
            address receiver,
            uint96 feeNumerator
        ) internal virtual {
            require(
                feeNumerator <= _feeDenominator(),
                "ERC2981: royalty fee will exceed salePrice"
            );
            require(receiver != address(0), "ERC2981: Invalid parameters");
    
            _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
        }
    
        function _resetTokenRoyalty(uint256 tokenId) internal virtual {
            delete _tokenRoyaltyInfo[tokenId];
        }
    }
    
    interface IERC721 is IERC165 {
        event Transfer(
            address indexed from,
            address indexed to,
            uint256 indexed tokenId
        );
        event Approval(
            address indexed owner,
            address indexed approved,
            uint256 indexed tokenId
        );
        event ApprovalForAll(
            address indexed owner,
            address indexed operator,
            bool approved
        );
    
        function balanceOf(address owner) external view returns (uint256 balance);
    
        function ownerOf(uint256 tokenId) external view returns (address owner);
    
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external;
    
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
    
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
    
        function approve(address to, uint256 tokenId) external;
    
        function setApprovalForAll(address operator, bool approved) external;
    
        function getApproved(uint256 tokenId)
            external
            view
            returns (address operator);
    
        function isApprovedForAll(address owner, address operator)
            external
            view
            returns (bool);
    }
    
    interface IERC4906 is IERC165, IERC721 {
        event MetadataUpdate(uint256 _tokenId);
        event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
    }
    
    interface IERC721Metadata is IERC721 {
        function name() external view returns (string memory);
    
        function symbol() external view returns (string memory);
    
        function tokenURI(uint256 tokenId) external view returns (string memory);
    }
    
    contract ERC721 is ERC165, IERC721 {
        string private _name;
        string private _symbol;
        string private _baseURI;
        mapping(uint256 => address) private _owners;
        mapping(address => uint256) private _balances;
        mapping(uint256 => address) private _tokenApprovals;
        mapping(address => mapping(address => bool)) private _operatorApprovals;
    
        constructor(
            string memory name_,
            string memory symbol_,
            string memory baseURI_
        ) {
            _name = name_;
            _symbol = symbol_;
            _baseURI = baseURI_;
        }
    
        function supportsInterface(bytes4 interfaceId)
            public
            view
            virtual
            override(ERC165, IERC165)
            returns (bool)
        {
            return
                interfaceId == type(IERC721).interfaceId ||
                interfaceId == type(IERC721Metadata).interfaceId ||
                super.supportsInterface(interfaceId);
        }
    
        function setBaseUri(string memory baseURI) internal virtual {
            _baseURI = baseURI;
        }
    
        function balanceOf(address owner)
            public
            view
            virtual
            override
            returns (uint256)
        {
            require(
                owner != address(0),
                "ERC721: address zero is not a valid owner"
            );
            return _balances[owner];
        }
    
        function ownerOf(uint256 tokenId)
            public
            view
            virtual
            override
            returns (address)
        {
            address owner = _ownerOf(tokenId);
            require(owner != address(0), "ERC721: invalid token ID");
            return owner;
        }
    
        function name() public view virtual returns (string memory) {
            return _name;
        }
    
        function symbol() public view virtual returns (string memory) {
            return _symbol;
        }
    
        function tokenURI(uint256 tokenId)
            public
            view
            virtual
            returns (string memory)
        {
            _requireMinted(tokenId);
            return
                bytes(_baseURI).length > 0
                    ? string(abi.encodePacked(_baseURI, Strings.toString(tokenId)))
                    : "";
        }
    
        function approve(address to, uint256 tokenId) public virtual override {
            address owner = ERC721.ownerOf(tokenId);
            require(to != owner, "ERC721: approval to current owner");
    
            require(
                msg.sender == owner || isApprovedForAll(owner, msg.sender),
                "ERC721: approve caller is not token owner or approved for all"
            );
    
            _approve(to, tokenId);
        }
    
        function getApproved(uint256 tokenId)
            public
            view
            virtual
            override
            returns (address)
        {
            _requireMinted(tokenId);
    
            return _tokenApprovals[tokenId];
        }
    
        function setApprovalForAll(address operator, bool approved)
            public
            virtual
            override
        {
            _setApprovalForAll(msg.sender, operator, approved);
        }
    
        function isApprovedForAll(address owner, address operator)
            public
            view
            virtual
            override
            returns (bool)
        {
            return _operatorApprovals[owner][operator];
        }
    
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            //solhint-disable-next-line max-line-length
            require(
                _isApprovedOrOwner(msg.sender, tokenId),
                "ERC721: caller is not token owner or approved"
            );
    
            _transfer(from, to, tokenId);
        }
    
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) public virtual override {
            safeTransferFrom(from, to, tokenId, "");
        }
    
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes memory data
        ) public virtual override {
            require(
                _isApprovedOrOwner(msg.sender, tokenId),
                "ERC721: caller is not token owner or approved"
            );
            _safeTransfer(from, to, tokenId, data);
        }
    
        function _safeTransfer(
            address from,
            address to,
            uint256 tokenId,
            bytes memory data
        ) internal virtual {
            _transfer(from, to, tokenId);
            require(
                _checkOnERC721Received(from, to, tokenId, data),
                "ERC721: transfer to non ERC721Receiver implementer"
            );
        }
    
        function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
            return _owners[tokenId];
        }
    
        function _exists(uint256 tokenId) internal view virtual returns (bool) {
            return _ownerOf(tokenId) != address(0);
        }
    
        function _isApprovedOrOwner(address spender, uint256 tokenId)
            internal
            view
            virtual
            returns (bool)
        {
            address owner = ERC721.ownerOf(tokenId);
            return (spender == owner ||
                isApprovedForAll(owner, spender) ||
                getApproved(tokenId) == spender);
        }
    
        function _safeMint(address to, uint256 tokenId) internal virtual {
            _safeMint(to, tokenId, "");
        }
    
        function _safeMint(
            address to,
            uint256 tokenId,
            bytes memory data
        ) internal virtual {
            _mint(to, tokenId);
            require(
                _checkOnERC721Received(address(0), to, tokenId, data),
                "ERC721: transfer to non ERC721Receiver implementer"
            );
        }
    
        function _mint(address to, uint256 tokenId) internal virtual {
            require(to != address(0), "ERC721: mint to the zero address");
            require(!_exists(tokenId), "ERC721: token already minted");
    
            _beforeTokenTransfer(address(0), to, tokenId, 1);
    
            require(!_exists(tokenId), "ERC721: token already minted");
    
            unchecked {
                _balances[to] += 1;
            }
    
            _owners[tokenId] = to;
    
            emit Transfer(address(0), to, tokenId);
    
            _afterTokenTransfer(address(0), to, tokenId, 1);
        }
    
        function _burn(uint256 tokenId) internal virtual {
            address owner = ERC721.ownerOf(tokenId);
    
            _beforeTokenTransfer(owner, address(0), tokenId, 1);
            owner = ERC721.ownerOf(tokenId);
            delete _tokenApprovals[tokenId];
    
            unchecked {
                _balances[owner] -= 1;
            }
            delete _owners[tokenId];
    
            emit Transfer(owner, address(0), tokenId);
    
            _afterTokenTransfer(owner, address(0), tokenId, 1);
        }
    
        function _transfer(
            address from,
            address to,
            uint256 tokenId
        ) internal virtual {
            require(
                ERC721.ownerOf(tokenId) == from,
                "ERC721: transfer from incorrect owner"
            );
            require(to != address(0), "ERC721: transfer to the zero address");
    
            _beforeTokenTransfer(from, to, tokenId, 1);
            require(
                ERC721.ownerOf(tokenId) == from,
                "ERC721: transfer from incorrect owner"
            );
            delete _tokenApprovals[tokenId];
    
            unchecked {
                _balances[from] -= 1;
                _balances[to] += 1;
            }
            _owners[tokenId] = to;
    
            emit Transfer(from, to, tokenId);
    
            _afterTokenTransfer(from, to, tokenId, 1);
        }
    
        function _approve(address to, uint256 tokenId) internal virtual {
            _tokenApprovals[tokenId] = to;
            emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
        }
    
        function _setApprovalForAll(
            address owner,
            address operator,
            bool approved
        ) internal virtual {
            require(owner != operator, "ERC721: approve to caller");
            _operatorApprovals[owner][operator] = approved;
            emit ApprovalForAll(owner, operator, approved);
        }
    
        function _requireMinted(uint256 tokenId) internal view virtual {
            require(_exists(tokenId), "ERC721: invalid token ID");
        }
    
        function _checkOnERC721Received(
            address from,
            address to,
            uint256 tokenId,
            bytes memory data
        ) private returns (bool) {
            if (isContract(to)) {
                try
                    IERC721Receiver(to).onERC721Received(
                        msg.sender,
                        from,
                        tokenId,
                        data
                    )
                returns (bytes4 retval) {
                    return retval == IERC721Receiver.onERC721Received.selector;
                } catch (bytes memory reason) {
                    if (reason.length == 0) {
                        revert(
                            "ERC721: transfer to non ERC721Receiver implementer"
                        );
                    } else {
                        /// @solidity memory-safe-assembly
                        assembly {
                            revert(add(32, reason), mload(reason))
                        }
                    }
                }
            } else {
                return true;
            }
        }
    
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 firstTokenId,
            uint256 batchSize
        ) internal virtual {}
    
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 firstTokenId,
            uint256 batchSize
        ) internal virtual {}
    
        function __unsafe_increaseBalance(address account, uint256 amount)
            internal
        {
            _balances[account] += amount;
        }
    
        function isContract(address account) internal view returns (bool) {
            return account.code.length > 0;
        }
    }
    
    contract CitizenZero is ERC721, TokenAccessControl, ERC2981, IERC4906 {
        using Counters for Counters.Counter;
        string private _contractURI;
        uint256 public mintLimit;
        uint64 public whitelistMintStart;
        uint64 public fcfsMintStart;
        uint64 public publicMintStart;
        uint64 public mintEnd;
        Counters.Counter private _tokenIdCounter;
        Counters.Counter private _orderCounter;
        mapping(uint256 => CitizenZeroData) private _citizenZeroData;
        mapping(address => bool) private _whitelist;
        mapping(address => bool) private _fcfs;
        mapping(address => uint16) private _mintedAmount;
    
        struct CitizenZeroData {
            string secretPhrase;
            uint256 orderNumber;
            uint16[] achievements;
        }
    
        constructor(
            string memory name_,
            string memory symbol_,
            string memory baseURI_,
            string memory contractURI_,
            uint64 whitelistMintStart_,
            uint64 fcfsMintStart_,
            uint64 publicMintStart_,
            uint64 mintEnd_,
            uint256 mintLimit_,
            address royaltyReceiver_,
            uint96 royaltyFeeNumerator_
        ) ERC721(name_, symbol_, baseURI_) {
            _contractURI = contractURI_;
            whitelistMintStart = whitelistMintStart_;
            fcfsMintStart = fcfsMintStart_;
            publicMintStart = publicMintStart_;
            mintEnd = mintEnd_;
            mintLimit = mintLimit_;
            _setDefaultRoyalty(royaltyReceiver_, royaltyFeeNumerator_);
            _mintCitizenZero(msg.sender);
        }
    
        function supportsInterface(bytes4 interfaceId)
            public
            view
            virtual
            override(ERC721, ERC2981, IERC165)
            returns (bool)
        {
            return
                ERC721.supportsInterface(interfaceId) ||
                ERC2981.supportsInterface(interfaceId) ||
                interfaceId == bytes4(0x49064906);
        }
    
        function batchMetadataUpdate(uint256 fromTokenId, uint256 toTokenId)
            public
            onlyOwnerOrAuthorizedUser
        {
            emit BatchMetadataUpdate(fromTokenId, toTokenId);
        }
    
        function metadataUpdate(uint256 tokenId) public {
            emit MetadataUpdate(tokenId);
        }
    
        function setDefaultRoyalty(address receiver, uint96 feeNumerator)
            public
            onlyOwner
        {
            _setDefaultRoyalty(receiver, feeNumerator);
        }
    
        function setWhitelistAddresses(
            address[] memory whitelistAddresses,
            bool approval
        ) public onlyOwnerOrAuthorizedUser {
            for (uint256 i = 0; i < whitelistAddresses.length; ++i) {
                _whitelist[whitelistAddresses[i]] = approval;
            }
        }
    
        function setFcfsAddresses(address[] memory fcfsAddresses, bool approval)
            public
            onlyOwnerOrAuthorizedUser
        {
            for (uint256 i = 0; i < fcfsAddresses.length; ++i) {
                _fcfs[fcfsAddresses[i]] = approval;
            }
        }
    
        function setUri(string memory baseURI) public onlyOwner {
            setBaseUri(baseURI);
        }
    
        function setContractUri(string memory contractUri) public onlyOwner {
            _contractURI = contractUri;
        }
    
        function contractURI() public view returns (string memory) {
            return _contractURI;
        }
    
        function totalSupply() public view virtual returns (uint256) {
            return _tokenIdCounter.current();
        }
    
        function mintedAmount(address account) public view returns (uint16) {
            return _mintedAmount[account];
        }
    
        function burn(uint256 tokenId) public virtual {
            require(
                _isApprovedOrOwner(msg.sender, tokenId),
                "ERC721: caller is not token owner or approved"
            );
            _burn(tokenId);
        }
    
        function addAchievement(uint256 tokenId, uint16 achievementId)
            public
            onlyOwnerOrAuthorizedUser
        {
            _requireMinted(tokenId);
            _citizenZeroData[tokenId].achievements.push(achievementId);
            emit MetadataUpdate(tokenId);
        }
    
        function setSecretPhrase(uint256 tokenId, string memory secretPhrase)
            public
            onlyOwnerOrAuthorizedUser
        {
            _requireMinted(tokenId);
            _orderCounter.increment();
            _citizenZeroData[tokenId] = CitizenZeroData(
                secretPhrase,
                _orderCounter.current(),
                _citizenZeroData[tokenId].achievements
            );
            emit MetadataUpdate(tokenId);
        }
    
        function setMintTimes(
            uint64 _whitelistMintStart,
            uint64 _fcfsMintStart,
            uint64 _publicMintStart,
            uint64 _mintEnd
        ) public onlyOwner {
            whitelistMintStart = _whitelistMintStart;
            fcfsMintStart = _fcfsMintStart;
            publicMintStart = _publicMintStart;
            mintEnd = _mintEnd;
        }
    
        function getCitizenZeroData(uint256 tokenId)
            public
            view
            returns (
                string memory secretPhrase,
                uint256 orderNumber,
                uint16[] memory achievements
            )
        {
            _requireMinted(tokenId);
            return (
                _citizenZeroData[tokenId].secretPhrase,
                _citizenZeroData[tokenId].orderNumber,
                _citizenZeroData[tokenId].achievements
            );
        }
    
        function _mintCitizenZero(address to) internal {
            _tokenIdCounter.increment();
            _mint(to, _tokenIdCounter.current());
        }
    
        function mint() public ifNotPaused {
            require(
                block.timestamp < mintEnd,
                "CitizenZero: Mint has already ended!"
            );
            require(
                this.totalSupply() < mintLimit,
                "CitizenZero: Mint limit reached!"
            );
            require(
                _mintedAmount[msg.sender] < 1,
                "CitizenZero: Mint limit already reached for this wallet!"
            );
            require(
                msg.sender == tx.origin,
                "CitizenZero: Mint is allowed only for wallets!"
            );
            _mintedAmount[msg.sender]++;
            if (block.timestamp > publicMintStart) {
                _mintCitizenZero(msg.sender);
            } else if (
                block.timestamp > fcfsMintStart && _fcfs[msg.sender] == true
            ) {
                _mintCitizenZero(msg.sender);
            } else if (
                block.timestamp > whitelistMintStart &&
                _whitelist[msg.sender] == true
            ) {
                _mintCitizenZero(msg.sender);
            } else {
                revert("CitizenZero: You are not eligible for mint!");
            }
        }
    
        function isWhitelisted(address account) public view returns (bool) {
            return _whitelist[account];
        }
    
        function isFcfs(address account) public view returns (bool) {
            return _fcfs[account];
        }
    
        function withdraw(address contractAddress, uint256 amount)
            public
            onlyOwner
        {
            if (contractAddress == address(0)) {
                payable(msg.sender).transfer(amount);
            } else {
                if (amount == 0)
                    amount = IERC20(contractAddress).balanceOf(address(this));
                IERC20(contractAddress).transfer(msg.sender, amount);
            }
        }
    
        receive() external payable {}
    
        fallback() external payable {}
    }