ETH Price: $2,418.07 (-1.01%)

Transaction Decoder

Block:
14950954 at Jun-12-2022 03:41:42 PM +UTC
Transaction Fee:
0.00849278736504704 ETH $20.54
Gas Used:
204,740 Gas / 41.480840896 Gwei

Emitted Events:

339 AnimeMetaverse.Transfer( _from=[Sender] 0x8e606ac138bf2a0b332ccc739c48fcfd84f5de58, _to=[Receiver] AmvNftStaker, _tokenId=3614 )
340 AmvNftStaker.NFTStaked( owner=[Sender] 0x8e606ac138bf2a0b332ccc739c48fcfd84f5de58, tokenIds=[3614], timestamp=1655048502, eventType=7374616B696E6700000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x68Cd21D3...A152B2379
(SBI Crypto Pool)
559.666736987877878259 Eth559.667044097877878259 Eth0.00030711
0x8E606ac1...d84F5DE58
0.106298280889686525 Eth
Nonce: 7
0.097805493524639485 Eth
Nonce: 8
0.00849278736504704
0x9ece0695...d22B8a26d

Execution Trace

AmvNftStaker.stake( tokenIds=[3614], timeLockType=3 )
  • AnimeMetaverse.ownerOf( _tokenId=3614 ) => ( _owner=0x8E606ac138bF2a0B332CCc739C48FCfd84F5DE58 )
  • AnimeMetaverse.transferFrom( _from=0x8E606ac138bF2a0B332CCc739C48FCfd84F5DE58, _to=0x9ece069523f02B69A042b15f58d3834d22B8a26d, _tokenId=3614 )
    File 1 of 2: AmvNftStaker
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.9;
    /**
     * @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;
        }
    }
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(
            address indexed previousOwner,
            address indexed newOwner
        );
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
            _;
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(
                newOwner != address(0),
                "Ownable: new owner is the zero address"
            );
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    interface IERC721Receiver {
        /**
         * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
         * by `operator` from `from`, this function is called.
         *
         * It must return its Solidity selector to confirm the token transfer.
         * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
         *
         * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
         */
        function onERC721Received(
            address operator,
            address from,
            uint256 tokenId,
            bytes calldata data
        ) external returns (bytes4);
    }
    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);
    }
    /**
     * @dev Required interface of an ERC721 compliant contract.
     */
    interface IERC721 is IERC165 {
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(
            address indexed from,
            address indexed to,
            uint256 indexed tokenId
        );
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(
            address indexed owner,
            address indexed approved,
            uint256 indexed tokenId
        );
        /**
         * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(
            address indexed owner,
            address indexed operator,
            bool approved
        );
        /**
         * @dev Returns the number of tokens in ``owner``'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external;
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
         * are aware of the ERC721 protocol to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Transfers `tokenId` token from `from` to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external;
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external;
        /**
         * @dev 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);
    }
    /**
     * @dev This is a smart contract which provides staking and unstaking facilities with time-lock only for AnimeMetaverse NftTokens.
     * Owners of AnimeMetaverse NftTokens can call `stake` function to stake their NftTokens and `unstake` function to unstake their NftTokens.
     * When owner of the NftTokens call `stake` function he provides a list of tokenIds and a time-lock type.
     * Based on the type of time-lock, Time-lock values are defined which can only be 0,30,60,90 days.
     * Once the NftTokens' owner call the staking function, the ownership of these NftTokens are trasferred from the owner address to this smart contract address.
     * The owner's address, current timestamp and time-lock are saved in a vault of this smart contrct.
     * Later, when the owner of these NftTokens call the unstake function with a list of NftTokenIds, Firstly, it is checked that whether this caller was the previous owner
     * of these NftTokens or not.
     * This checking is done with the data saved in `vault`.
     * Then the time-lock validation is checked.
     * If these checking are done, then the NftTokens ownership is given back to the orginal owner from this smart contract address.
     */
    contract AmvNftStaker is Ownable {
        // Stores counts of staked NFT tokens.
        uint256 public totalStaked;
        // Flag to enable or disable the staking.
        bool public isOpenForStaking = true;
        // Flag to enable or disable the time-lock checking during unstaking.
        bool public isTimeLockActive = true;
        // Stores maximum length of batch staking/unstaking tokenIds array.
        uint256 public maxInputSize = 10;
        // Maximum token size to be set for batch staking and unstaking.
        uint256 public constant allowedMaxInputSize = 100;
        // Stores AMV smart contract details.
        IERC721 public nft;
        // String constant to determine the event type.
        bytes32 public constant stakingEventType = "staking";
        // String constant to determine the event type.
        bytes32 public constant unStakingEventType = "unstaking";
        // TimeLock for NFT staking
        // Value of ZERO_DAY = 0
        // Value of THIRTY_DAYS = 1
        // Value of SIXTY_DAYS = 2
        // Value of NINETY_DAYS = 3
        enum TimeLock {
            ZERO_DAY,
            THIRTY_DAYS,
            SIXTY_DAYS,
            NINETY_DAYS
        }
        // Struct to store a stake's tokenId, address of the owner and function execution timestamp and the token's owner defined time-lock for unstaking.
        struct Stake {
            uint256 tokenId;
            address owner;
            uint256 stakedAt;
            uint256 timeLock;
        }
        // Stores all possible value of time-locks
        uint256[4] public timeLocks = [0, 30 * 86400, 60 * 86400, 90 * 86400];
        // Maps tokenId to stake details.
        mapping(uint256 => Stake) public vault;
        // List of tokens that have been staked at least once.
        uint256[] public nftTokenIds;
        // Maps tokenId to bool to check if tokenId has been staked at least once.
        mapping(uint256 => bool) public tokenIdExist;
        /**
         * @dev Emits when the NFTs are staked.
         * @param owner The address of the owner of these NFTs.
         * @param tokenIds The tokenIDs of these NFTs.
         * @param timestamp The execution timestamp of the staking function.
         * @param eventType The Type of this event.
         */
        event NFTStaked(
            address owner,
            uint256[] tokenIds,
            uint256 timestamp,
            bytes32 eventType
        );
        /**
         * @dev Emits when the NFTs are unstaked.
         * @param owner The address of the owner of these NFTs.
         * @param tokenIds The tokenIDs of these NFTs.
         * @param timestamp The execution timestamp of the unstaking function.
         * @param eventType The Type of this event.
         */
        event NFTUnstaked(
            address owner,
            uint256[] tokenIds,
            uint256 timestamp,
            bytes32 eventType
        );
        /**
         * @dev Initializes the contract.
         * Creates instance of AnimeMetaverse smart contract through constructor.
         */
        constructor(address amvAddress) {
            nft = IERC721(amvAddress);
        }
        /**
         * @notice Only Owner of this smart contract is allowed to call this function.
         * @dev public function to set the maximum length of batch staking/unstaking tokenIds array.
         */
        function setMaxInputSize(uint256 _maxInputSize) public onlyOwner {
            /**
             * @dev Throws if _maxInputSize is greater than 100.
             */
            require(
                _maxInputSize <= allowedMaxInputSize,
                "Can not set MaxInputSize more than 100"
            );
            /**
             * @dev Throws if _maxInputSize is less than 1.
             */
            require(_maxInputSize >= 1, "Can not set MaxInputSize less than 1");
            maxInputSize = _maxInputSize;
        }
        /**
         * @notice Only Owner of this smart contract is allowed to call this function.
         * @dev public function to change the value of `isOpenForStaking` flag which decides whether staking to this smart contract is allowed or not .
         */
        function setIsOpenForStaking(bool _isOpenForStaking) public onlyOwner {
            isOpenForStaking = _isOpenForStaking;
        }
        /**
         * @notice Only Owner of this smart contract is allowed to call this function.
         * @dev public function to change the value of `isTimeLockActive` flag which decides whether time-lock will be considered during unstaking or not .
         */
        function setIsTimeLockActive(bool _isTimeLockActive) public onlyOwner {
            isTimeLockActive = _isTimeLockActive;
        }
        /**
         * @notice Use this function with caution. Wrong usage can have serious consequences.
         * @dev external function to stake AnimeMetaverse NFTs from owner address of these NFTs to this smart contract address.
         * @param tokenIds uint256[] tokenIDs of the AnimeMetaverse NFTs to be staked to this smart contract address.
         */
        function stake(uint256[] calldata tokenIds, uint8 timeLockType) external {
            /**
             * @dev Throws if the `isOpenForStaking` is false.
             */
            require(isOpenForStaking, "Staking is not allowed");
            /**
             * @dev Throws if the `timeLockType` is not between 0 and 3.
             */
            require(
                timeLockType == uint8(TimeLock.ZERO_DAY) ||
                    timeLockType == uint8(TimeLock.THIRTY_DAYS) ||
                    timeLockType == uint8(TimeLock.SIXTY_DAYS) ||
                    timeLockType == uint8(TimeLock.NINETY_DAYS),
                "Invalid timeLock type"
            );
            /**
             * @dev Throws if the `tokenIds` is empty or count of `tokenIds` is more than 50.
             */
            require(tokenIds.length > 0, "Input parameter array is empty");
            require(
                tokenIds.length <= maxInputSize,
                "Maximum Input size of tokenIds is exceded"
            );
            uint256 tokenId;
            for (uint256 i = 0; i < tokenIds.length; i++) {
                tokenId = tokenIds[i];
                /**
                 * Getting owner's address of this tokenId from AnimeMetaverse NFT smart contract.
                 * @dev Throws if `nftOwnerAddress` doesn't match with `msg.sender`.
                 */
                address nftOwnerAddress = nft.ownerOf(tokenId);
                require(
                    nftOwnerAddress == msg.sender,
                    "Sender is not the owner of the token"
                );
                /**
                 * @dev Throws if the tokenId of this NFT is already staked.
                 */
                require(vault[tokenId].tokenId == 0, "Token is already staked");
                /**
                 * @dev Transfers the ownership of an NFT from `msg.sender` to `address(this)`.
                 * `address(this)` means this smart contract address.
                 */
                nft.transferFrom(msg.sender, address(this), tokenId);
                addNewNftToVault(tokenId, timeLocks[timeLockType]);
                addNewTokenIdToList(tokenId); //
            }
            totalStaked += tokenIds.length; // Updating the count of total staked NFT tokens.
            emit NFTStaked(msg.sender, tokenIds, block.timestamp, stakingEventType); // emiting NFTStaked event.
        }
        /**
         * @dev Private function to add NFT information to the `vault`.
         * Stores tokenId, address of the owner and function execution timestamp against tokenId using map.
         * @param tokenId uint256 tokenID of the AnimeMetaverse nfts to be added to the `vault`.
         */
        function addNewNftToVault(uint256 tokenId, uint256 timeLock) private {
            vault[tokenId] = Stake({
                owner: msg.sender,
                tokenId: tokenId,
                stakedAt: uint256(block.timestamp),
                timeLock: timeLock
            });
        }
        /**
         * @dev Private function to add tokenIds to `nftTokenIds` list.
         * Checks if this tokenId is already added to `nftTokenIds` list or not.
         * If if this tokenId is not already added to `nftTokenIds` , sets the flag for this `tokenId` true and adds to the `nftTokenIds` list
         * @param tokenId uint256 tokenID of the AnimeMetaverse nfts to be added to the `nftTokenIds` list.
         */
        function addNewTokenIdToList(uint256 tokenId) private {
            if (!tokenIdExist[tokenId]) {
                tokenIdExist[tokenId] = true;
                nftTokenIds.push(tokenId);
            }
        }
        /**
         * @notice Use this function with caution. Wrong usage can have serious consequences.
         * @dev External function to unstake AnimeMetaverse NFTs from this smart contract address to the owner of these NFTs tokenIds.
         * @param tokenIds uint256[] tokenIDs of the AnimeMetaverse NFTs to be unstaked from this smart contract address.
         */
        function unstake(uint256[] calldata tokenIds) external {
            /**
             * @dev Throws if the `tokenIds` is empty or count of `tokenIds` is more than 50.
             */
            require(tokenIds.length > 0, "Input parameter array is empty");
            require(
                tokenIds.length <= maxInputSize,
                "Maximum input size of tokenIds is exceded"
            );
            uint256 tokenId;
            totalStaked -= tokenIds.length; // updating the count of total staked NFT tokens.
            for (uint256 i = 0; i < tokenIds.length; i++) {
                tokenId = tokenIds[i];
                /**
                 * Getting stake information from the vault for this tokenId.
                 * @dev Throws if `staked.owner` doesn't match with `msg.sender`.
                 * Here, staked.owner is the owner address of this tokenId which is stored in our vault.
                 */
                Stake memory staked = vault[tokenId];
                require(
                    staked.owner == msg.sender,
                    "Sender is not the owner of these tokens"
                );
                /**
                 * @dev Throws if this smart contract is not the owner of the token.
                 */
                address nftOwnerAddress = nft.ownerOf(tokenId);
                require(
                    nftOwnerAddress == address(this),
                    "This smart contract is not the owner of these tokens"
                );
                timeLockCheck(staked.stakedAt, staked.timeLock);
                removeNftFromVault(tokenId);
                /**
                 * @dev Transfers the ownership of an NFT from `address(this)` to`msg.sender`.
                 * Here, `address(this)` means this smart contract address.
                 */
                nft.transferFrom(address(this), msg.sender, tokenId);
            }
            emit NFTUnstaked(
                msg.sender,
                tokenIds,
                block.timestamp,
                unStakingEventType
            ); //emiting NFTUnstaked event.
        }
        /**
         * @dev Public function to check if a token is eligible to unstake.
         * @param stakedAt uint256 staking timestamp of a token stored in `vault`.
         * @param timeLock uint256 time-lock of a token set by token's owner during staking which is stored in `vault`.
         */
        function timeLockCheck(uint256 stakedAt, uint256 timeLock) public view {
            /**
             * @dev Throws if `isTimeLockActive` is true and the differnce between the current timestamp and staking timestamp is not greater than tokens owner's predefined time-lock.
             */
            if (isTimeLockActive) {
                require(
                    (block.timestamp - stakedAt) > timeLock,
                    "Tokens cannot be unstaked before its chosen minimum time lock period"
                );
            }
        }
        /**
         * @dev Private function to delete NFT information from the `vault`.
         * @param tokenId uint256 tokenID of the AnimeMetaverse nfts to be added to the `vault`.
         */
        function removeNftFromVault(uint256 tokenId) private {
            delete vault[tokenId];
        }
        /**
         * @dev Public function to get a list of NFTs which are staked in our smart contract.
         * Checks every stake stored in this `vault` against this `account`
         * If the owner of any stake matches with this `account`, then collects them in a list and are returned.
         * @param account address The address that owns the NFTs.
         * @return ownrTokens A list of tokens owned by `account` from `vault`
         */
        function tokensOfOwner(address account)
            public
            view
            returns (Stake[] memory ownrTokens)
        {
            uint256 supply = nftTokenIds.length;
            Stake[] memory tmp = new Stake[](supply);
            uint256 nftCount = 0;
            for (uint256 i = 0; i < supply; i++) {
                Stake memory staked = vault[nftTokenIds[i]];
                if (staked.owner == account) {
                    tmp[nftCount] = staked;
                    nftCount += 1;
                }
            }
            Stake[] memory ownerTokens = new Stake[](nftCount);
            for (uint256 i = 0; i < nftCount; i++) {
                ownerTokens[i] = tmp[i];
            }
            return ownerTokens;
        }
        /**
         * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
         * by `operator` from `from`, this function is called.
         *
         * It must return its Solidity selector to confirm the token transfer.
         * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
         *
         * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
         */
        function onERC721Received(
            address,
            address from,
            uint256,
            bytes calldata
        ) external pure returns (bytes4) {
            require(from == address(0x0), "Cannot send nfts to Vault directly");
            return IERC721Receiver.onERC721Received.selector;
        }
    }
    

    File 2 of 2: AnimeMetaverse
    // SPDX-License-Identifier: Unlicense
    pragma solidity ^0.8.9;
    
    library AddressUtils {
    
      /**
       * Returns whether the target address is a contract
       * @dev This function will return false if invoked during the constructor of a contract,
       * as the code is not actually created until after the constructor finishes.
       * @param _addr address to check
       * @return whether the target address is a contract
       */
      function isContract(address _addr) internal view returns (bool) {
        uint256 size;
        // XXX Currently there is no better way to check if there is a contract in an address
        // than to check the size of the code at that address.
        // See https://ethereum.stackexchange.com/a/14016/36603
        // for more details about how this works.
        // TODO Check this again before the Serenity release, because all addresses will be
        // contracts then.
        // solium-disable-next-line security/no-inline-assembly
        assembly { size := extcodesize(_addr) }
        return size > 0;
      }
    
    }
    
    library MerkleProof {
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
      )
        internal
        pure
        returns (bool)
      {
        bytes32 computedHash = leaf;
    
        for (uint256 i = 0; i < proof.length; i++) {
          bytes32 proofElement = proof[i];
    
          if (computedHash < proofElement) {
            // Hash(current computed hash + current element of the proof)
            computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
          } else {
            // Hash(current element of the proof + current computed hash)
            computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
          }
        }
    
        // Check if the computed hash (root) is equal to the provided root
        return computedHash == root;
      }
    }
    
    /**
     * @dev Optional metadata extension for ERC-721 non-fungible token standard.
     * See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md.
     */
    interface ERC721Metadata
    {
    
      /**
       * @dev Returns a descriptive name for a collection of NFTs in this contract.
       * @return _name Representing name.
       */
      function name()
        external
        view
        returns (string memory _name);
    
      /**
       * @dev Returns a abbreviated name for a collection of NFTs in this contract.
       * @return _symbol Representing symbol.
       */
      function symbol()
        external
        view
        returns (string memory _symbol);
    
    }
    
    
    /**
     * @dev ERC-721 interface for accepting safe transfers.
     * See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md.
     */
    interface ERC721TokenReceiver
    {
    
      /**
       * @notice The contract address is always the message sender. A wallet/broker/auction application
       * MUST implement the wallet interface if it will accept safe transfers.
       * @dev Handle the receipt of a NFT. The ERC721 smart contract calls this function on the
       * recipient after a `transfer`. This function MAY throw to revert and reject the transfer. Return
       * of other than the magic value MUST result in the transaction being reverted.
       * Returns `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` unless throwing.
       * @param _operator The address which called `safeTransferFrom` function.
       * @param _from The address which previously owned the token.
       * @param _tokenId The NFT identifier which is being transferred.
       * @param _data Additional data with no specified format.
       * @return Returns `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
       */
      function onERC721Received(
        address _operator,
        address _from,
        uint256 _tokenId,
        bytes calldata _data
      )
        external
        returns(bytes4);
    
    }
    
    
    /**
     * @dev ERC-721 non-fungible token standard.
     * See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md.
     */
    interface ERC721
    {
    
      /**
       * @dev Emits when ownership of any NFT changes by any mechanism. This event emits when NFTs are
       * created (`from` == 0) and destroyed (`to` == 0). Exception: during contract creation, any
       * number of NFTs may be created and assigned without emitting Transfer. At the time of any
       * transfer, the approved address for that NFT (if any) is reset to none.
       */
      event Transfer(
        address indexed _from,
        address indexed _to,
        uint256 indexed _tokenId
      );
    
      /**
       * @dev This emits when the approved address for an NFT is changed or reaffirmed. The zero
       * address indicates there is no approved address. When a Transfer event emits, this also
       * indicates that the approved address for that NFT (if any) is reset to none.
       */
      event Approval(
        address indexed _owner,
        address indexed _approved,
        uint256 indexed _tokenId
      );
    
      /**
       * @dev This emits when an operator is enabled or disabled for an owner. The operator can manage
       * all NFTs of the owner.
       */
      event ApprovalForAll(
        address indexed _owner,
        address indexed _operator,
        bool _approved
      );
    
      /**
       * @notice Throws unless `msg.sender` is the current owner, an authorized operator, or the
       * approved address for this NFT. Throws if `_from` is not the current owner. Throws if `_to` is
       * the zero address. Throws if `_tokenId` is not a valid NFT. When transfer is complete, this
       * function checks if `_to` is a smart contract (code size > 0). If so, it calls
       * `onERC721Received` on `_to` and throws if the return value is not
       * `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`.
       * @dev Transfers the ownership of an NFT from one address to another address. This function can
       * be changed to payable.
       * @param _from The current owner of the NFT.
       * @param _to The new owner.
       * @param _tokenId The NFT to transfer.
       * @param _data Additional data with no specified format, sent in call to `_to`.
       */
      function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes calldata _data
      )
        external;
    
      /**
       * @notice This works identically to the other function with an extra data parameter, except this
       * function just sets data to ""
       * @dev Transfers the ownership of an NFT from one address to another address. This function can
       * be changed to payable.
       * @param _from The current owner of the NFT.
       * @param _to The new owner.
       * @param _tokenId The NFT to transfer.
       */
      function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId
      )
        external;
    
      /**
       * @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else
       * they may be permanently lost.
       * @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved
       * address for this NFT. Throws if `_from` is not the current owner. Throws if `_to` is the zero
       * address. Throws if `_tokenId` is not a valid NFT.  This function can be changed to payable.
       * @param _from The current owner of the NFT.
       * @param _to The new owner.
       * @param _tokenId The NFT to transfer.
       */
      function transferFrom(
        address _from,
        address _to,
        uint256 _tokenId
      )
        external;
    
      /**
       * @notice The zero address indicates there is no approved address. Throws unless `msg.sender` is
       * the current NFT owner, or an authorized operator of the current owner.
       * @param _approved The new approved NFT controller.
       * @dev Set or reaffirm the approved address for an NFT. This function can be changed to payable.
       * @param _tokenId The NFT to approve.
       */
      function approve(
        address _approved,
        uint256 _tokenId
      )
        external;
    
      /**
       * @notice The contract MUST allow multiple operators per owner.
       * @dev Enables or disables approval for a third party ("operator") to manage all of
       * `msg.sender`'s assets. It also emits the ApprovalForAll event.
       * @param _operator Address to add to the set of authorized operators.
       * @param _approved True if the operators is approved, false to revoke approval.
       */
      function setApprovalForAll(
        address _operator,
        bool _approved
      )
        external;
    
      /**
       * @dev Returns the number of NFTs owned by `_owner`. NFTs assigned to the zero address are
       * considered invalid, and this function throws for queries about the zero address.
       * @notice Count all NFTs assigned to an owner.
       * @param _owner Address for whom to query the balance.
       * @return Balance of _owner.
       */
      function balanceOf(
        address _owner
      )
        external
        view
        returns (uint256);
    
      /**
       * @notice Find the owner of an NFT.
       * @dev Returns the address of the owner of the NFT. NFTs assigned to the zero address are
       * considered invalid, and queries about them do throw.
       * @param _tokenId The identifier for an NFT.
       * @return Address of _tokenId owner.
       */
      function ownerOf(
        uint256 _tokenId
      )
        external
        view
        returns (address);
    
      /**
       * @notice Throws if `_tokenId` is not a valid NFT.
       * @dev Get the approved address for a single NFT.
       * @param _tokenId The NFT to find the approved address for.
       * @return Address that _tokenId is approved for.
       */
      function getApproved(
        uint256 _tokenId
      )
        external
        view
        returns (address);
    
      /**
       * @notice Query if an address is an authorized operator for another address.
       * @dev Returns true if `_operator` is an approved operator for `_owner`, false otherwise.
       * @param _owner The address that owns the NFTs.
       * @param _operator The address that acts on behalf of the owner.
       * @return True if approved for all, false otherwise.
       */
      function isApprovedForAll(
        address _owner,
        address _operator
      )
        external
        view
        returns (bool);
    
    }
    
    
    /**
     * @dev A standard for detecting smart contract interfaces. 
     * See: https://eips.ethereum.org/EIPS/eip-165.
     */
    interface ERC165
    {
    
      /**
       * @dev Checks if the smart contract includes a specific interface.
       * This function uses less than 30,000 gas.
       * @param _interfaceID The interface identifier, as specified in ERC-165.
       * @return True if _interfaceID is supported, false otherwise.
       */
      function supportsInterface(
        bytes4 _interfaceID
      )
        external
        view
        returns (bool);
        
    }
    
    
    /**
     * @dev Implementation of standard for detect smart contract interfaces.
     */
    contract SupportsInterface is
      ERC165
    {
    
      /**
       * @dev Mapping of supported intefraces. You must not set element 0xffffffff to true.
       */
      mapping(bytes4 => bool) internal supportedInterfaces;
    
      /**
       * @dev Contract constructor.
       */
      constructor()
      {
        supportedInterfaces[0x01ffc9a7] = true; // ERC165
      }
    
      /**
       * @dev Function to check which interfaces are suported by this contract.
       * @param _interfaceID Id of the interface.
       * @return True if _interfaceID is supported, false otherwise.
       */
      function supportsInterface(
        bytes4 _interfaceID
      )
        external
        override
        view
        returns (bool)
      {
        return supportedInterfaces[_interfaceID];
      }
    
    }
    
    /**
     * @dev The contract has an owner address, and provides basic authorization control whitch
     * simplifies the implementation of user permissions. This contract is based on the source code at:
     * https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ownership/Ownable.sol
     */
    contract Ownable
    {
    
      /**
       * @dev Error constants.
       */
      string public constant NOT_CURRENT_OWNER = "018001";
      string public constant CANNOT_TRANSFER_TO_ZERO_ADDRESS = "018002";
    
      /**
       * @dev Current owner address.
       */
      address public owner;
    
      /**
       * @dev An event which is triggered when the owner is changed.
       * @param previousOwner The address of the previous owner.
       * @param newOwner The address of the new owner.
       */
      event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
      );
    
      /**
       * @dev The constructor sets the original `owner` of the contract to the sender account.
       */
      constructor()
      {
        owner = msg.sender;
      }
    
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner()
      {
        require(msg.sender == owner, NOT_CURRENT_OWNER);
        _;
      }
    
      /**
       * @dev Allows the current owner to transfer control of the contract to a newOwner.
       * @param _newOwner The address to transfer ownership to.
       */
      function transferOwnership(
        address _newOwner
      )
        public
        onlyOwner
      {
        require(_newOwner != address(0), CANNOT_TRANSFER_TO_ZERO_ADDRESS);
        emit OwnershipTransferred(owner, _newOwner);
        owner = _newOwner;
      }
    
    }
    
    
    /**
     * @dev Implementation of ERC-721 non-fungible token standard.
     */
    contract NFToken is
      ERC721,
      SupportsInterface
    {
      using AddressUtils for address;
    
      /**
       * @dev List of revert message codes. Implementing dApp should handle showing the correct message.
       * Based on 0xcert framework error codes.
       */
      string constant ZERO_ADDRESS = "003001";
      string constant NOT_VALID_NFT = "003002";
      string constant NOT_OWNER_OR_OPERATOR = "003003";
      string constant NOT_OWNER_APPROVED_OR_OPERATOR = "003004";
      string constant NOT_ABLE_TO_RECEIVE_NFT = "003005";
      string constant NFT_ALREADY_EXISTS = "003006";
      string constant NOT_OWNER = "003007";
      string constant IS_OWNER = "003008";
    
      /**
       * @dev Magic value of a smart contract that can receive NFT.
       * Equal to: bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")).
       */
      bytes4 internal constant MAGIC_ON_ERC721_RECEIVED = 0x150b7a02;
    
      /**
       * @dev A mapping from NFT ID to the address that owns it.
       */
      mapping (uint256 => address) internal idToOwner;
    
      /**
       * @dev Mapping from NFT ID to approved address.
       */
      mapping (uint256 => address) internal idToApproval;
    
       /**
       * @dev Mapping from owner address to count of their tokens.
       */
      mapping (address => uint256) private ownerToNFTokenCount;
    
      /**
       * @dev Mapping from owner address to mapping of operator addresses.
       */
      mapping (address => mapping (address => bool)) internal ownerToOperators;
    
      /**
       * @dev Guarantees that the msg.sender is an owner or operator of the given NFT.
       * @param _tokenId ID of the NFT to validate.
       */
      modifier canOperate(
        uint256 _tokenId
      )
      {
        address tokenOwner = idToOwner[_tokenId];
        require(
          tokenOwner == msg.sender || ownerToOperators[tokenOwner][msg.sender],
          NOT_OWNER_OR_OPERATOR
        );
        _;
      }
    
      /**
       * @dev Guarantees that the msg.sender is allowed to transfer NFT.
       * @param _tokenId ID of the NFT to transfer.
       */
      modifier canTransfer(
        uint256 _tokenId
      )
      {
        address tokenOwner = idToOwner[_tokenId];
        require(
          tokenOwner == msg.sender
          || idToApproval[_tokenId] == msg.sender
          || ownerToOperators[tokenOwner][msg.sender],
          NOT_OWNER_APPROVED_OR_OPERATOR
        );
        _;
      }
    
      /**
       * @dev Guarantees that _tokenId is a valid Token.
       * @param _tokenId ID of the NFT to validate.
       */
      modifier validNFToken(
        uint256 _tokenId
      )
      {
        require(idToOwner[_tokenId] != address(0), NOT_VALID_NFT);
        _;
      }
    
      /**
       * @dev Contract constructor.
       */
      constructor()
      {
        supportedInterfaces[0x80ac58cd] = true; // ERC721
      }
    
      /**
       * @notice Throws unless `msg.sender` is the current owner, an authorized operator, or the
       * approved address for this NFT. Throws if `_from` is not the current owner. Throws if `_to` is
       * the zero address. Throws if `_tokenId` is not a valid NFT. When transfer is complete, this
       * function checks if `_to` is a smart contract (code size > 0). If so, it calls
       * `onERC721Received` on `_to` and throws if the return value is not
       * `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`.
       * @dev Transfers the ownership of an NFT from one address to another address. This function can
       * be changed to payable.
       * @param _from The current owner of the NFT.
       * @param _to The new owner.
       * @param _tokenId The NFT to transfer.
       * @param _data Additional data with no specified format, sent in call to `_to`.
       */
      function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes calldata _data
      )
        external
        override
      {
        _safeTransferFrom(_from, _to, _tokenId, _data);
      }
    
      /**
       * @notice This works identically to the other function with an extra data parameter, except this
       * function just sets data to "".
       * @dev Transfers the ownership of an NFT from one address to another address. This function can
       * be changed to payable.
       * @param _from The current owner of the NFT.
       * @param _to The new owner.
       * @param _tokenId The NFT to transfer.
       */
      function safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId
      )
        external
        override
      {
        _safeTransferFrom(_from, _to, _tokenId, "");
      }
    
      /**
       * @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else
       * they may be permanently lost.
       * @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved
       * address for this NFT. Throws if `_from` is not the current owner. Throws if `_to` is the zero
       * address. Throws if `_tokenId` is not a valid NFT. This function can be changed to payable.
       * @param _from The current owner of the NFT.
       * @param _to The new owner.
       * @param _tokenId The NFT to transfer.
       */
      function transferFrom(
        address _from,
        address _to,
        uint256 _tokenId
      )
        external
        override
        canTransfer(_tokenId)
        validNFToken(_tokenId)
      {
        address tokenOwner = idToOwner[_tokenId];
        require(tokenOwner == _from, NOT_OWNER);
        require(_to != address(0), ZERO_ADDRESS);
    
        _transfer(_to, _tokenId);
      }
    
      /**
       * @notice The zero address indicates there is no approved address. Throws unless `msg.sender` is
       * the current NFT owner, or an authorized operator of the current owner.
       * @dev Set or reaffirm the approved address for an NFT. This function can be changed to payable.
       * @param _approved Address to be approved for the given NFT ID.
       * @param _tokenId ID of the token to be approved.
       */
      function approve(
        address _approved,
        uint256 _tokenId
      )
        external
        override
        canOperate(_tokenId)
        validNFToken(_tokenId)
      {
        address tokenOwner = idToOwner[_tokenId];
        require(_approved != tokenOwner, IS_OWNER);
    
        idToApproval[_tokenId] = _approved;
        emit Approval(tokenOwner, _approved, _tokenId);
      }
    
      /**
       * @notice This works even if sender does not own any tokens at the time.
       * @dev Enables or disables approval for a third party ("operator") to manage all of
       * `msg.sender`'s assets. It also emits the ApprovalForAll event.
       * @param _operator Address to add to the set of authorized operators.
       * @param _approved True if the operators is approved, false to revoke approval.
       */
      function setApprovalForAll(
        address _operator,
        bool _approved
      )
        external
        override
      {
        ownerToOperators[msg.sender][_operator] = _approved;
        emit ApprovalForAll(msg.sender, _operator, _approved);
      }
    
      /**
       * @dev Returns the number of NFTs owned by `_owner`. NFTs assigned to the zero address are
       * considered invalid, and this function throws for queries about the zero address.
       * @param _owner Address for whom to query the balance.
       * @return Balance of _owner.
       */
      function balanceOf(
        address _owner
      )
        external
        override
        view
        returns (uint256)
      {
        require(_owner != address(0), ZERO_ADDRESS);
        return _getOwnerNFTCount(_owner);
      }
    
      /**
       * @dev Returns the address of the owner of the NFT. NFTs assigned to the zero address are
       * considered invalid, and queries about them do throw.
       * @param _tokenId The identifier for an NFT.
       * @return _owner Address of _tokenId owner.
       */
      function ownerOf(
        uint256 _tokenId
      )
        external
        override
        view
        returns (address _owner)
      {
        _owner = idToOwner[_tokenId];
        require(_owner != address(0), NOT_VALID_NFT);
      }
      
      function ownerOfInternal(
        uint256 _tokenId
      )
        internal
        view
        returns (address _owner)
      {
        _owner = idToOwner[_tokenId];
        require(_owner != address(0), NOT_VALID_NFT);
      }
    
      /**
       * @notice Throws if `_tokenId` is not a valid NFT.
       * @dev Get the approved address for a single NFT.
       * @param _tokenId ID of the NFT to query the approval of.
       * @return Address that _tokenId is approved for.
       */
      function getApproved(
        uint256 _tokenId
      )
        external
        override
        view
        validNFToken(_tokenId)
        returns (address)
      {
        return idToApproval[_tokenId];
      }
    
      /**
       * @dev Checks if `_operator` is an approved operator for `_owner`.
       * @param _owner The address that owns the NFTs.
       * @param _operator The address that acts on behalf of the owner.
       * @return True if approved for all, false otherwise.
       */
      function isApprovedForAll(
        address _owner,
        address _operator
      )
        external
        override
        view
        returns (bool)
      {
        return ownerToOperators[_owner][_operator];
      }
    
      /**
       * @notice Does NO checks.
       * @dev Actually performs the transfer.
       * @param _to Address of a new owner.
       * @param _tokenId The NFT that is being transferred.
       */
      function _transfer(
        address _to,
        uint256 _tokenId
      )
        internal
      {
        address from = idToOwner[_tokenId];
        _clearApproval(_tokenId);
    
        _removeNFToken(from, _tokenId);
        _addNFToken(_to, _tokenId);
    
        emit Transfer(from, _to, _tokenId);
      }
    
      /**
       * @notice This is an internal function which should be called from user-implemented external
       * mint function. Its purpose is to show and properly initialize data structures when using this
       * implementation.
       * @dev Mints a new NFT.
       * @param _to The address that will own the minted NFT.
       * @param _tokenId of the NFT to be minted by the msg.sender.
       */
      function _mint(
        address _to,
        uint256 _tokenId
      )
        internal
        virtual
      {
        require(_to != address(0), ZERO_ADDRESS);
        require(idToOwner[_tokenId] == address(0), NFT_ALREADY_EXISTS);
    
        _addNFToken(_to, _tokenId);
    
        emit Transfer(address(0), _to, _tokenId);
      }
    
      /**
       * @notice This is an internal function which should be called from user-implemented external burn
       * function. Its purpose is to show and properly initialize data structures when using this
       * implementation. Also, note that this burn implementation allows the minter to re-mint a burned
       * NFT.
       * @dev Burns a NFT.
       * @param _tokenId ID of the NFT to be burned.
       */
      function _burn(
        uint256 _tokenId
      )
        internal
        virtual
        validNFToken(_tokenId)
      {
        address tokenOwner = idToOwner[_tokenId];
        _clearApproval(_tokenId);
        _removeNFToken(tokenOwner, _tokenId);
        emit Transfer(tokenOwner, address(0), _tokenId);
      }
    
      /**
       * @notice Use and override this function with caution. Wrong usage can have serious consequences.
       * @dev Removes a NFT from owner.
       * @param _from Address from which we want to remove the NFT.
       * @param _tokenId Which NFT we want to remove.
       */
      function _removeNFToken(
        address _from,
        uint256 _tokenId
      )
        internal
        virtual
      {
        require(idToOwner[_tokenId] == _from, NOT_OWNER);
        ownerToNFTokenCount[_from] -= 1;
        delete idToOwner[_tokenId];
      }
    
      /**
       * @notice Use and override this function with caution. Wrong usage can have serious consequences.
       * @dev Assigns a new NFT to owner.
       * @param _to Address to which we want to add the NFT.
       * @param _tokenId Which NFT we want to add.
       */
      function _addNFToken(
        address _to,
        uint256 _tokenId
      )
        internal
        virtual
      {
        require(idToOwner[_tokenId] == address(0), NFT_ALREADY_EXISTS);
    
        idToOwner[_tokenId] = _to;
        ownerToNFTokenCount[_to] += 1;
      }
    
      /**
       * @dev Helper function that gets NFT count of owner. This is needed for overriding in enumerable
       * extension to remove double storage (gas optimization) of owner NFT count.
       * @param _owner Address for whom to query the count.
       * @return Number of _owner NFTs.
       */
      function _getOwnerNFTCount(
        address _owner
      )
        internal
        virtual
        view
        returns (uint256)
      {
        return ownerToNFTokenCount[_owner];
      }
    
      /**
       * @dev Actually perform the safeTransferFrom.
       * @param _from The current owner of the NFT.
       * @param _to The new owner.
       * @param _tokenId The NFT to transfer.
       * @param _data Additional data with no specified format, sent in call to `_to`.
       */
      function _safeTransferFrom(
        address _from,
        address _to,
        uint256 _tokenId,
        bytes memory _data
      )
        private
        canTransfer(_tokenId)
        validNFToken(_tokenId)
      {
        address tokenOwner = idToOwner[_tokenId];
        require(tokenOwner == _from, NOT_OWNER);
        require(_to != address(0), ZERO_ADDRESS);
    
        _transfer(_to, _tokenId);
    
        if (_to.isContract())
        {
          bytes4 retval = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data);
          require(retval == MAGIC_ON_ERC721_RECEIVED, NOT_ABLE_TO_RECEIVE_NFT);
        }
      }
    
      /**
       * @dev Clears the current approval of a given NFT ID.
       * @param _tokenId ID of the NFT to be transferred.
       */
      function _clearApproval(
        uint256 _tokenId
      )
        private
      {
        delete idToApproval[_tokenId];
      }
    
    }
    
    
    
    /**
     * @dev Optional metadata implementation for ERC-721 non-fungible token standard.
     */
    contract NFTokenMetadata is
      NFToken,
      ERC721Metadata
    {
    
      /**
       * @dev A descriptive name for a collection of NFTs.
       */
      string internal nftName;
    
      /**
       * @dev An abbreviated name for NFTokens.
       */
      string internal nftSymbol;
    
    
      /**
       * @notice When implementing this contract don't forget to set nftName and nftSymbol.
       * @dev Contract constructor.
       */
      constructor()
      {
        supportedInterfaces[0x5b5e139f] = true; // ERC721Metadata
      }
    
      /**
       * @dev Returns a descriptive name for a collection of NFTokens.
       * @return _name Representing name.
       */
      function name()
        external
        override
        view
        returns (string memory _name)
      {
        _name = nftName;
      }
    
      /**
       * @dev Returns an abbreviated name for NFTokens.
       * @return _symbol Representing symbol.
       */
      function symbol()
        external
        override
        view
        returns (string memory _symbol)
      {
        _symbol = nftSymbol;
      }
    
      /**
       * @dev A distinct URI (RFC 3986) for a given NFT.
       * @param _tokenId Id for which we want uri.
       * @return URI of _tokenId.
       */
    
    
    }
    
    // OmegaM
    
    contract AnimeMetaverse is NFTokenMetadata, Ownable 
    {
        // Properties
        address payable feeAddress;
    
        // OmegaM
        uint constant public nftPrice = 0.25 ether;
        uint constant public maxNft   = 5000;   // Total nfts
        
        // Switches
        bool public isMintingActive     = false;  // Is minting open? Yes or no.
        bool public isWhiteListActive   = false;  // Is the public sale open or is it only whitelisted?
        bool public isMerkleActive      = true;   // OmegaM
    
        // Mappings 
        mapping(address => bool) private    whiteList; // Map of addresses on the whitelist.
        mapping(address => uint256) public  nftClaimed; // Map of how many nfts are minted per address.
    
        // Vars
        uint256 public current_minted = 0;
        // OmegaM
        uint public mintLimit = 1;
        
        // URI Data
        // OmegaM
        string private metaAddress = "https://mint.animemetaverse.ai/metadata/";
        string constant private jsonAppend = ".json";
    
        // Events
        event Minted(address sender, uint256 count);
    
        // Merkle tree support
    
        bytes32 public merkleRoot;
    
        constructor()
        {
            // OmegaM
            nftName     = "Anime Metaverse: Soulmates";
            nftSymbol   = "AMS";
            feeAddress  = payable(msg.sender);
        }
    
        function tokenURI(uint tokenID) external view returns (string memory)
        {   // @dev Token URIs are generated dynamically on view requests. 
            // This is to allow easy server changes and reduce gas fees for minting. -ssa2
            require(tokenID > 0, "Token does not exist.");
            require(tokenID <= current_minted, "Token hasn't been minted yet.");
    
            bytes32 thisToken;
            bytes memory concat;
            thisToken = uintToBytes(tokenID);
            concat = abi.encodePacked(metaAddress, thisToken, jsonAppend);
            return string(concat);
        }
    
        function setMerkleRoot (bytes32 merkle) external onlyOwner {
          require(merkle[0] != 0, "merkle root value is invalid");
    
            merkleRoot = merkle;
        }
    
    
        // Toggle whether any gals can be minted at all.
        function toggleMinting() public onlyOwner
        {
            isMintingActive = !isMintingActive;
        }
    
        // Toggle if we're in the Whitelist or Public Sale.
        function toggleWhiteList() public onlyOwner
        {
            isWhiteListActive = !isWhiteListActive;
        }
    
        function toggleMerkle () public onlyOwner {
          isMerkleActive = !isMerkleActive;      
        }
    
        // Add a list of wallet addresses to the Whitelist.
        function addToWhiteList(address[] calldata addresses) external onlyOwner {
            for (uint256 i = 0; i < addresses.length; i++) {
                require(addresses[i] != address(0), "Cannot add the null address");
    
                whiteList[addresses[i]] = true;
            }
        }
    
        // Tells the world if a given address is whitelisted or not.
        function onWhiteList(address addr) external view returns (bool) {
            return whiteList[addr];
        }
    
        // }:)
        function removeFromwhiteList(address[] calldata addresses) external onlyOwner {
            for (uint256 i = 0; i < addresses.length; i++) {
                require(addresses[i] != address(0), "Cant add null address");
    
                whiteList[addresses[i]] = false;
            }
        }
    
        // Public annoucement how many nfts a given address has minted.
        function claimedBy(address owner) external view returns (uint256) {
            require(owner != address(0), 'The zero address cannot mint anything.');
    
            return nftClaimed[owner];
        }
    
        // Address  ETH gets sent to when withdrawing.
        function updateRecipient(address payable _newAddress) public onlyOwner
        {
            feeAddress = _newAddress;
        }
    
        // Takes care of converting an integer into the raw bytes of it's abi-encodable string.
        function uintToBytes(uint v) private pure returns (bytes32 ret) {
            if (v == 0)
            {
                ret = '0';
            }
            else
            {
                while (v > 0) 
                {
                    ret = bytes32(uint(ret) / (2 ** 8));
                    ret |= bytes32(((v % 10) + 48) * 2 ** (8 * 31));
                    v /= 10;
                }
            }
            return ret;
        }
    
        function verify(bytes32[] calldata proof)
            public
            view
            returns (bool)
        {
            require(merkleRoot[0] != 0, "merkle root not set");
            bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
            return MerkleProof.verify(proof, merkleRoot, leaf);
        }
    
        // Public Sale minting function
        function mint(uint8 _mintNum, bytes32[] calldata _proof) public payable
        {
            require(isMintingActive, "Minting not available yet or paused for next round");
            require(_mintNum > 0, "At least one art is needed");
            require(_mintNum + nftClaimed[msg.sender] <= mintLimit, "Can't mint more than 1 limit per Wallet.");
            require(msg.value >= nftPrice * _mintNum, "NFTs are expensive, need more ETH to afford that.");
            require(current_minted + _mintNum <= maxNft, "Not enough of those left in stock.");
            require(_proof.length <= 10, "Invalid proof");
            
            if (isMerkleActive)
            {
                require(verify(_proof), "Not in the Merkle tree, only for exclusive invitees");
            }
    
            if(isWhiteListActive)
            {
                require(whiteList[msg.sender], "Only for exclusive invitees...");
            }
    
            for(uint i = 0; i < _mintNum; i++)
            {
                current_minted += 1;
                super._mint(msg.sender, current_minted);
                nftClaimed[msg.sender] += 1;
            }
    
            emit Minted(msg.sender, _mintNum);
        }
    
        // Emergency Devmint function if something gets messed up.
        function devMint(uint8 _mintNum) external onlyOwner 
        {
            require(_mintNum + current_minted <= maxNft, "Cannot mint more the total supply.");
            for(uint256 i = 0; i < _mintNum; i++) 
            {
                current_minted += 1;
                nftClaimed[msg.sender] += 1;
                super._mint(msg.sender, current_minted);
            }
        }
    
        // Withdraw the ETH stored in the contract.
        function withdrawETH() external onlyOwner 
        {
            feeAddress.transfer(address(this).balance);
        }
    
        // Update the metadata URI to a new server or IPFS if needed.
        function updateURI(string calldata _URI) external onlyOwner 
        {
            metaAddress = _URI;
        }
    
        // Update how many can be purchased at a time if need be.
        function updateLimit(uint newLimit) external onlyOwner
        {
            mintLimit = newLimit;
        }
    
        // Get how many nfts are minted right now.
        function totalSupply() public view returns(uint) 
        {
            return current_minted;
        }
    
        // Get how many nfts can be minted.    
        function maxSupply() public pure returns(uint) 
        {
            return maxNft;
        }
    }