Transaction Hash:
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 | ||
---|---|---|---|---|---|
0x68Cd21D3...A152B2379 | |||||
0x8B4de256...EE2813dbB
Miner
| (SBI Crypto Pool) | 559.666736987877878259 Eth | 559.667044097877878259 Eth | 0.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 )
stake[AmvNftStaker (ln:386)]
ownerOf[AmvNftStaker (ln:416)]
transferFrom[AmvNftStaker (ln:429)]
addNewNftToVault[AmvNftStaker (ln:430)]
addNewTokenIdToList[AmvNftStaker (ln:431)]
NFTStaked[AmvNftStaker (ln:434)]
File 1 of 2: AmvNftStaker
File 2 of 2: AnimeMetaverse
// 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; } }