ETH Price: $2,422.66 (-1.52%)

Transaction Decoder

Block:
14303597 at Mar-01-2022 09:39:34 PM +UTC
Transaction Fee:
0.005714053971253602 ETH $13.84
Gas Used:
121,498 Gas / 47.030024949 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x364C828e...99aD972ec
(F2Pool Old)
3,085.932415193415399525 Eth3,085.933268546360938229 Eth0.000853352945538704
0x90C66150...2057B0e30
0.098586420675128793 Eth
Nonce: 717
0.092872366703875191 Eth
Nonce: 718
0.005714053971253602
0xdf8A8821...D3616b57A

Execution Trace

StakeSeals.deposit( tokenIds=[8548] )
  • SappySeals.safeTransferFrom( from=0x90C6615019920aDAc4Ab3eF64F7ECf62057B0e30, to=0xdf8A88212FF229446e003f8f879e263D3616b57A, tokenId=8548, _data=0x )
    • StakeSeals.onERC721Received( 0xdf8A88212FF229446e003f8f879e263D3616b57A, 0x90C6615019920aDAc4Ab3eF64F7ECf62057B0e30, 8548, 0x )
      File 1 of 2: StakeSeals
      // SPDX-License-Identifier: MIT
      
      pragma solidity ^0.8.0;
      
      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 `IERC721.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);
      }
      
      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`, 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 be 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 Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
      
          /**
           * @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 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 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;
      }
      
      library Math {
          /**
           * @dev Returns the largest of two numbers.
           */
          function max(uint256 a, uint256 b) internal pure returns (uint256) {
              return a >= b ? a : b;
          }
      
          /**
           * @dev Returns the smallest of two numbers.
           */
          function min(uint256 a, uint256 b) internal pure returns (uint256) {
              return a < b ? a : b;
          }
      
          /**
           * @dev Returns the average of two numbers. The result is rounded towards
           * zero.
           */
          function average(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b) / 2 can overflow.
              return (a & b) + (a ^ b) / 2;
          }
      
          /**
           * @dev Returns the ceiling of the division of two numbers.
           *
           * This differs from standard division with `/` in that it rounds up instead
           * of rounding down.
           */
          function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b - 1) / b can overflow on addition, so we distribute.
              return a / b + (a % b == 0 ? 0 : 1);
          }
      }
      
      library EnumerableSet {
          // To implement this library for multiple types with as little code
          // repetition as possible, we write it in terms of a generic Set type with
          // bytes32 values.
          // The Set implementation uses private functions, and user-facing
          // implementations (such as AddressSet) are just wrappers around the
          // underlying Set.
          // This means that we can only create new EnumerableSets for types that fit
          // in bytes32.
      
          struct Set {
              // Storage of set values
              bytes32[] _values;
              // Position of the value in the `values` array, plus 1 because index 0
              // means a value is not in the set.
              mapping(bytes32 => uint256) _indexes;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function _add(Set storage set, bytes32 value) private returns (bool) {
              if (!_contains(set, value)) {
                  set._values.push(value);
                  // The value is stored at length-1, but we add 1 to all indexes
                  // and use 0 as a sentinel value
                  set._indexes[value] = set._values.length;
                  return true;
              } else {
                  return false;
              }
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function _remove(Set storage set, bytes32 value) private returns (bool) {
              // We read and store the value's index to prevent multiple reads from the same storage slot
              uint256 valueIndex = set._indexes[value];
      
              if (valueIndex != 0) {
                  // Equivalent to contains(set, value)
                  // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                  // the array, and then remove the last element (sometimes called as 'swap and pop').
                  // This modifies the order of the array, as noted in {at}.
      
                  uint256 toDeleteIndex = valueIndex - 1;
                  uint256 lastIndex = set._values.length - 1;
      
                  if (lastIndex != toDeleteIndex) {
                      bytes32 lastvalue = set._values[lastIndex];
      
                      // Move the last value to the index where the value to delete is
                      set._values[toDeleteIndex] = lastvalue;
                      // Update the index for the moved value
                      set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
                  }
      
                  // Delete the slot where the moved value was stored
                  set._values.pop();
      
                  // Delete the index for the deleted slot
                  delete set._indexes[value];
      
                  return true;
              } else {
                  return false;
              }
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function _contains(Set storage set, bytes32 value) private view returns (bool) {
              return set._indexes[value] != 0;
          }
      
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function _length(Set storage set) private view returns (uint256) {
              return set._values.length;
          }
      
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function _at(Set storage set, uint256 index) private view returns (bytes32) {
              return set._values[index];
          }
      
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function _values(Set storage set) private view returns (bytes32[] memory) {
              return set._values;
          }
      
          // Bytes32Set
      
          struct Bytes32Set {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _add(set._inner, value);
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _remove(set._inner, value);
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
              return _contains(set._inner, value);
          }
      
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(Bytes32Set storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
              return _at(set._inner, index);
          }
      
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
              return _values(set._inner);
          }
      
          // AddressSet
      
          struct AddressSet {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(AddressSet storage set, address value) internal returns (bool) {
              return _add(set._inner, bytes32(uint256(uint160(value))));
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(AddressSet storage set, address value) internal returns (bool) {
              return _remove(set._inner, bytes32(uint256(uint160(value))));
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(AddressSet storage set, address value) internal view returns (bool) {
              return _contains(set._inner, bytes32(uint256(uint160(value))));
          }
      
          /**
           * @dev Returns the number of values in the set. O(1).
           */
          function length(AddressSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(AddressSet storage set, uint256 index) internal view returns (address) {
              return address(uint160(uint256(_at(set._inner, index))));
          }
      
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function values(AddressSet storage set) internal view returns (address[] memory) {
              bytes32[] memory store = _values(set._inner);
              address[] memory result;
      
              assembly {
                  result := store
              }
      
              return result;
          }
      
          // UintSet
      
          struct UintSet {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(UintSet storage set, uint256 value) internal returns (bool) {
              return _add(set._inner, bytes32(value));
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(UintSet storage set, uint256 value) internal returns (bool) {
              return _remove(set._inner, bytes32(value));
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(UintSet storage set, uint256 value) internal view returns (bool) {
              return _contains(set._inner, bytes32(value));
          }
      
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function length(UintSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(UintSet storage set, uint256 index) internal view returns (uint256) {
              return uint256(_at(set._inner, index));
          }
      
          /**
           * @dev Return the entire set in an array
           *
           * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
           * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
           * this function has an unbounded cost, and using it as part of a state-changing function may render the function
           * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
           */
          function values(UintSet storage set) internal view returns (uint256[] memory) {
              bytes32[] memory store = _values(set._inner);
              uint256[] memory result;
      
              assembly {
                  result := store
              }
      
              return result;
          }
      }
      
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
      
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      
      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() {
              _setOwner(_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 {
              _setOwner(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");
              _setOwner(newOwner);
          }
      
          function _setOwner(address newOwner) private {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      
      interface IPIXL {
          function whitelist_mint(address account, uint256 amount) external;
      }
      
      contract StakeSeals is IERC721Receiver, Ownable {
          using EnumerableSet for EnumerableSet.UintSet;
      
          address public ERC20_CONTRACT;
          address public ERC721_CONTRACT;
          uint256 public EXPIRATION; //expiry block number (avg 15s per block)
      
          mapping(address => EnumerableSet.UintSet) private _deposits;
          mapping(address => mapping(uint256 => uint256)) public depositBlocks;
          mapping (uint256 => uint256) public tokenRarity;
          uint256[7] public rewardRate;   
          bool started;
      
          constructor(
              address _erc20,
              address _erc721,
              uint256 _expiration
          ) {
              ERC20_CONTRACT = _erc20;
              ERC721_CONTRACT = _erc721;
              EXPIRATION = block.number + _expiration;
              // number of tokens Per day
              rewardRate = [50, 60, 75, 100, 150, 500, 0];
              started = false;
          }
      
          function setRate(uint256 _rarity, uint256 _rate) public onlyOwner() {
              rewardRate[_rarity] = _rate;
          }
      
          function setRarity(uint256 _tokenId, uint256 _rarity) public onlyOwner() {
              tokenRarity[_tokenId] = _rarity;
          }
      
          function setBatchRarity(uint256[] memory _tokenIds, uint256 _rarity) public onlyOwner() {
              for (uint256 i; i < _tokenIds.length; i++) {
                  uint256 tokenId = _tokenIds[i];
                  tokenRarity[tokenId] = _rarity;
              }
          }
      
          function setExpiration(uint256 _expiration) public onlyOwner() {
              EXPIRATION = _expiration;
          }
      
          
          function toggleStart() public onlyOwner() {
              started = !started;
          }
      
          function setTokenAddress(address _tokenAddress) public onlyOwner() {
              // Used to change rewards token if needed
              ERC20_CONTRACT = _tokenAddress;
          }
      
          function onERC721Received(
              address,
              address,
              uint256,
              bytes calldata
          ) external pure override returns (bytes4) {
              return IERC721Receiver.onERC721Received.selector;
          }
      
          function depositsOf(address account)
              external
              view
              returns (uint256[] memory)
          {
              EnumerableSet.UintSet storage depositSet = _deposits[account];
              uint256[] memory tokenIds = new uint256[](depositSet.length());
      
              for (uint256 i; i < depositSet.length(); i++) {
                  tokenIds[i] = depositSet.at(i);
              }
      
              return tokenIds;
          }
      
          function findRate(uint256 tokenId)
              public
              view
              returns (uint256 rate) 
          {
              uint256 rarity = tokenRarity[tokenId];
              uint256 perDay = rewardRate[rarity];
              
              // 6000 blocks per day
              // perDay / 6000 = reward per block
      
              rate = (perDay * 1e18) / 6000;
              
              return rate;
          }
      
          function calculateRewards(address account, uint256[] memory tokenIds)
              public
              view
              returns (uint256[] memory rewards)
          {
              rewards = new uint256[](tokenIds.length);
      
              for (uint256 i; i < tokenIds.length; i++) {
                  uint256 tokenId = tokenIds[i];
                  uint256 rate = findRate(tokenId);
                  rewards[i] =
                      rate *
                      (_deposits[account].contains(tokenId) ? 1 : 0) *
                      (Math.min(block.number, EXPIRATION) -
                          depositBlocks[account][tokenId]);
              }
          }
      
          function claimRewards(uint256[] calldata tokenIds) public {
              uint256 reward;
              uint256 curblock = Math.min(block.number, EXPIRATION);
      
              uint256[] memory rewards = calculateRewards(msg.sender, tokenIds);
      
              for (uint256 i; i < tokenIds.length; i++) {
                  reward += rewards[i];
                  depositBlocks[msg.sender][tokenIds[i]] = curblock;
              }
      
              if (reward > 0) {
                  IPIXL(ERC20_CONTRACT).whitelist_mint(msg.sender, reward);
              }
          }
      
          function deposit(uint256[] calldata tokenIds) external {
              require(started, 'StakeSeals: Staking contract not started yet');
      
              claimRewards(tokenIds);
              
      
              for (uint256 i; i < tokenIds.length; i++) {
                  IERC721(ERC721_CONTRACT).safeTransferFrom(
                      msg.sender,
                      address(this),
                      tokenIds[i],
                      ''
                  );
                  _deposits[msg.sender].add(tokenIds[i]);
              }
          }
      
          function admin_deposit(uint256[] calldata tokenIds) onlyOwner() external {
              claimRewards(tokenIds);
              
      
              for (uint256 i; i < tokenIds.length; i++) {
                  IERC721(ERC721_CONTRACT).safeTransferFrom(
                      msg.sender,
                      address(this),
                      tokenIds[i],
                      ''
                  );
                  _deposits[msg.sender].add(tokenIds[i]);
              }
          }
      
          function withdraw(uint256[] calldata tokenIds) external {
              claimRewards(tokenIds);
      
              for (uint256 i; i < tokenIds.length; i++) {
                  require(
                      _deposits[msg.sender].contains(tokenIds[i]),
                      'StakeSeals: Token not deposited'
                  );
      
                  _deposits[msg.sender].remove(tokenIds[i]);
      
                  IERC721(ERC721_CONTRACT).safeTransferFrom(
                      address(this),
                      msg.sender,
                      tokenIds[i],
                      ''
                  );
              }
          }
      }

      File 2 of 2: SappySeals
      // Sources flattened with hardhat v2.5.0 https://hardhat.org
       
      // File contracts/utils/introspection/IERC165.sol
       
      // SPDX-License-Identifier: MIT
       
      pragma solidity ^0.8.0;
       
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
       
       
      // File contracts/token/ERC721/IERC721.sol
       
       
       
      pragma solidity ^0.8.0;
       
      /**
       * @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`, 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 be 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 Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
       
          /**
           * @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 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 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;
      }
       
       
      // File contracts/token/ERC721/IERC721Receiver.sol
       
       
       
      pragma solidity ^0.8.0;
       
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      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 `IERC721.onERC721Received.selector`.
           */
          function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
      }
       
       
      // File contracts/token/ERC721/extensions/IERC721Metadata.sol
       
       
       
      pragma solidity ^0.8.0;
       
      /**
       * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
       * @dev See https://eips.ethereum.org/EIPS/eip-721
       */
      interface IERC721Metadata is IERC721 {
       
          /**
           * @dev Returns the token collection name.
           */
          function name() external view returns (string memory);
       
          /**
           * @dev Returns the token collection symbol.
           */
          function symbol() external view returns (string memory);
       
          /**
           * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
           */
          function tokenURI(uint256 tokenId) external view returns (string memory);
      }
       
       
      // File contracts/utils/Address.sol
       
       
       
      pragma solidity ^0.8.0;
       
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
       
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
       
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
       
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
       
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
       
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
       
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
       
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
       
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: value }(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
       
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
       
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
       
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.staticcall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
       
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
       
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
       
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
       
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
       
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
       
       
      // File contracts/utils/Context.sol
       
       
       
      pragma solidity ^0.8.0;
       
      /*
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
       
          function _msgData() internal view virtual returns (bytes calldata) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
       
       
      // File contracts/utils/Strings.sol
       
       
       
      pragma solidity ^0.8.0;
       
      /**
       * @dev String operations.
       */
      library Strings {
          bytes16 private constant alphabet = "0123456789abcdef";
       
          /**
           * @dev Converts a `uint256` to its ASCII `string` decimal representation.
           */
          function toString(uint256 value) internal pure returns (string memory) {
              // Inspired by OraclizeAPI's implementation - MIT licence
              // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
       
              if (value == 0) {
                  return "0";
              }
              uint256 temp = value;
              uint256 digits;
              while (temp != 0) {
                  digits++;
                  temp /= 10;
              }
              bytes memory buffer = new bytes(digits);
              while (value != 0) {
                  digits -= 1;
                  buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                  value /= 10;
              }
              return string(buffer);
          }
       
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
           */
          function toHexString(uint256 value) internal pure returns (string memory) {
              if (value == 0) {
                  return "0x00";
              }
              uint256 temp = value;
              uint256 length = 0;
              while (temp != 0) {
                  length++;
                  temp >>= 8;
              }
              return toHexString(value, length);
          }
       
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
           */
          function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
              bytes memory buffer = new bytes(2 * length + 2);
              buffer[0] = "0";
              buffer[1] = "x";
              for (uint256 i = 2 * length + 1; i > 1; --i) {
                  buffer[i] = alphabet[value & 0xf];
                  value >>= 4;
              }
              require(value == 0, "Strings: hex length insufficient");
              return string(buffer);
          }
       
      }
       
       
      // File contracts/utils/introspection/ERC165.sol
       
       
       
      pragma solidity ^0.8.0;
       
      /**
       * @dev Implementation of the {IERC165} interface.
       *
       * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
       * for the additional interface id that will be supported. For example:
       *
       * ```solidity
       * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
       *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
       * }
       * ```
       *
       * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
       */
      abstract contract ERC165 is IERC165 {
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              return interfaceId == type(IERC165).interfaceId;
          }
      }
       
       
      // File contracts/token/ERC721/ERC721.sol
       
       
       
      pragma solidity ^0.8.0;
       
       
       
       
       
       
       
      /**
       * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
       * the Metadata extension, but not including the Enumerable extension, which is available separately as
       * {ERC721Enumerable}.
       */
      contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
          using Address for address;
          using Strings for uint256;
       
          // Token name
          string private _name;
       
          // Token symbol
          string private _symbol;
       
          // Mapping from token ID to owner address
          mapping (uint256 => address) private _owners;
       
          // Mapping owner address to token count
          mapping (address => uint256) private _balances;
       
          // Mapping from token ID to approved address
          mapping (uint256 => address) private _tokenApprovals;
       
          // Mapping from owner to operator approvals
          mapping (address => mapping (address => bool)) private _operatorApprovals;
       
          /**
           * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
           */
          constructor (string memory name_, string memory symbol_) {
              _name = name_;
              _symbol = symbol_;
          }
       
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
              return interfaceId == type(IERC721).interfaceId
                  || interfaceId == type(IERC721Metadata).interfaceId
                  || super.supportsInterface(interfaceId);
          }
       
          /**
           * @dev See {IERC721-balanceOf}.
           */
          function balanceOf(address owner) public view virtual override returns (uint256) {
              require(owner != address(0), "ERC721: balance query for the zero address");
              return _balances[owner];
          }
       
          /**
           * @dev See {IERC721-ownerOf}.
           */
          function ownerOf(uint256 tokenId) public view virtual override returns (address) {
              address owner = _owners[tokenId];
              require(owner != address(0), "ERC721: owner query for nonexistent token");
              return owner;
          }
       
          /**
           * @dev See {IERC721Metadata-name}.
           */
          function name() public view virtual override returns (string memory) {
              return _name;
          }
       
          /**
           * @dev See {IERC721Metadata-symbol}.
           */
          function symbol() public view virtual override returns (string memory) {
              return _symbol;
          }
       
          /**
           * @dev See {IERC721Metadata-tokenURI}.
           */
          function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
              require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
       
              string memory baseURI = _baseURI();
              return bytes(baseURI).length > 0
                  ? string(abi.encodePacked(baseURI, tokenId.toString()))
                  : '';
          }
       
          /**
           * @dev Base URI for computing {tokenURI}. Empty by default, can be overriden
           * in child contracts.
           */
          function _baseURI() internal view virtual returns (string memory) {
              return "";
          }
       
          /**
           * @dev See {IERC721-approve}.
           */
          function approve(address to, uint256 tokenId) public virtual override {
              address owner = ERC721.ownerOf(tokenId);
              require(to != owner, "ERC721: approval to current owner");
       
              require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                  "ERC721: approve caller is not owner nor approved for all"
              );
       
              _approve(to, tokenId);
          }
       
          /**
           * @dev See {IERC721-getApproved}.
           */
          function getApproved(uint256 tokenId) public view virtual override returns (address) {
              require(_exists(tokenId), "ERC721: approved query for nonexistent token");
       
              return _tokenApprovals[tokenId];
          }
       
          /**
           * @dev See {IERC721-setApprovalForAll}.
           */
          function setApprovalForAll(address operator, bool approved) public virtual override {
              require(operator != _msgSender(), "ERC721: approve to caller");
       
              _operatorApprovals[_msgSender()][operator] = approved;
              emit ApprovalForAll(_msgSender(), operator, approved);
          }
       
          /**
           * @dev See {IERC721-isApprovedForAll}.
           */
          function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
              return _operatorApprovals[owner][operator];
          }
       
          /**
           * @dev See {IERC721-transferFrom}.
           */
          function transferFrom(address from, address to, uint256 tokenId) public virtual override {
              //solhint-disable-next-line max-line-length
              require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
       
              _transfer(from, to, tokenId);
          }
       
          /**
           * @dev See {IERC721-safeTransferFrom}.
           */
          function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
              safeTransferFrom(from, to, tokenId, "");
          }
       
          /**
           * @dev See {IERC721-safeTransferFrom}.
           */
          function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
              require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
              _safeTransfer(from, to, tokenId, _data);
          }
       
          /**
           * @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.
           *
           * `_data` is additional data, it has no specified format and it is sent in call to `to`.
           *
           * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
           * implement alternative mechanisms to perform token transfer, such as signature-based.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
              _transfer(from, to, tokenId);
              require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
          }
       
          /**
           * @dev Returns whether `tokenId` exists.
           *
           * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
           *
           * Tokens start existing when they are minted (`_mint`),
           * and stop existing when they are burned (`_burn`).
           */
          function _exists(uint256 tokenId) internal view virtual returns (bool) {
              return _owners[tokenId] != address(0);
          }
       
          /**
           * @dev Returns whether `spender` is allowed to manage `tokenId`.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
              require(_exists(tokenId), "ERC721: operator query for nonexistent token");
              address owner = ERC721.ownerOf(tokenId);
              return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
          }
       
          /**
           * @dev Safely mints `tokenId` and transfers it to `to`.
           *
           * Requirements:
           *
           * - `tokenId` must not exist.
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function _safeMint(address to, uint256 tokenId) internal virtual {
              _safeMint(to, tokenId, "");
          }
       
          /**
           * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
           * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
           */
          function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
              _mint(to, tokenId);
              require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
          }
       
          /**
           * @dev Mints `tokenId` and transfers it to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
           *
           * Requirements:
           *
           * - `tokenId` must not exist.
           * - `to` cannot be the zero address.
           *
           * Emits a {Transfer} event.
           */
          function _mint(address to, uint256 tokenId) internal virtual {
              require(to != address(0), "ERC721: mint to the zero address");
              require(!_exists(tokenId), "ERC721: token already minted");
       
              _beforeTokenTransfer(address(0), to, tokenId);
       
              _balances[to] += 1;
              _owners[tokenId] = to;
       
              emit Transfer(address(0), to, tokenId);
          }
       
          /**
           * @dev Destroys `tokenId`.
           * The approval is cleared when the token is burned.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           *
           * Emits a {Transfer} event.
           */
          function _burn(uint256 tokenId) internal virtual {
              address owner = ERC721.ownerOf(tokenId);
       
              _beforeTokenTransfer(owner, address(0), tokenId);
       
              // Clear approvals
              _approve(address(0), tokenId);
       
              _balances[owner] -= 1;
              delete _owners[tokenId];
       
              emit Transfer(owner, address(0), tokenId);
          }
       
          /**
           * @dev Transfers `tokenId` from `from` to `to`.
           *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           *
           * Emits a {Transfer} event.
           */
          function _transfer(address from, address to, uint256 tokenId) internal virtual {
              require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
              require(to != address(0), "ERC721: transfer to the zero address");
       
              _beforeTokenTransfer(from, to, tokenId);
       
              // Clear approvals from the previous owner
              _approve(address(0), tokenId);
       
              _balances[from] -= 1;
              _balances[to] += 1;
              _owners[tokenId] = to;
       
              emit Transfer(from, to, tokenId);
          }
       
          /**
           * @dev Approve `to` to operate on `tokenId`
           *
           * Emits a {Approval} event.
           */
          function _approve(address to, uint256 tokenId) internal virtual {
              _tokenApprovals[tokenId] = to;
              emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
          }
       
          /**
           * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
           * The call is not executed if the target address is not a contract.
           *
           * @param from address representing the previous owner of the given token ID
           * @param to target address that will receive the tokens
           * @param tokenId uint256 ID of the token to be transferred
           * @param _data bytes optional data to send along with the call
           * @return bool whether the call correctly returned the expected magic value
           */
          function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
              private returns (bool)
          {
              if (to.isContract()) {
                  try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                      return retval == IERC721Receiver(to).onERC721Received.selector;
                  } catch (bytes memory reason) {
                      if (reason.length == 0) {
                          revert("ERC721: transfer to non ERC721Receiver implementer");
                      } else {
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              revert(add(32, reason), mload(reason))
                          }
                      }
                  }
              } else {
                  return true;
              }
          }
       
          /**
           * @dev Hook that is called before any token transfer. This includes minting
           * and burning.
           *
           * Calling conditions:
           *
           * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
           * transferred to `to`.
           * - When `from` is zero, `tokenId` will be minted for `to`.
           * - When `to` is zero, ``from``'s `tokenId` will be burned.
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
      }
       
       
      // File contracts/token/ERC721/extensions/ERC721URIStorage.sol
       
       
       
      pragma solidity ^0.8.0;
       
      /**
       * @dev ERC721 token with storage based token URI management.
       */
      abstract contract ERC721URIStorage is ERC721 {
          using Strings for uint256;
       
          // Optional mapping for token URIs
          mapping (uint256 => string) private _tokenURIs;
       
          /**
           * @dev See {IERC721Metadata-tokenURI}.
           */
          function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
              require(_exists(tokenId), "ERC721URIStorage: URI query for nonexistent token");
       
              string memory _tokenURI = _tokenURIs[tokenId];
              string memory base = _baseURI();
       
              // If there is no base URI, return the token URI.
              if (bytes(base).length == 0) {
                  return _tokenURI;
              }
              // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
              if (bytes(_tokenURI).length > 0) {
                  return string(abi.encodePacked(base, _tokenURI));
              }
       
              return super.tokenURI(tokenId);
          }
       
          /**
           * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
              require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
              _tokenURIs[tokenId] = _tokenURI;
          }
       
          /**
           * @dev Destroys `tokenId`.
           * The approval is cleared when the token is burned.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           *
           * Emits a {Transfer} event.
           */
          function _burn(uint256 tokenId) internal virtual override {
              super._burn(tokenId);
       
              if (bytes(_tokenURIs[tokenId]).length != 0) {
                  delete _tokenURIs[tokenId];
              }
          }
      }
       
       
      // File contracts/access/Ownable.sol
       
       
       
      pragma solidity ^0.8.0;
       
      /**
       * @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 () {
              address msgSender = _msgSender();
              _owner = msgSender;
              emit OwnershipTransferred(address(0), 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 {
              emit OwnershipTransferred(_owner, address(0));
              _owner = 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");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
       
       
       
      pragma solidity ^0.8.0;
       
       
       
      contract SappySeals is ERC721URIStorage, Ownable{
       
          event MintSeals (address indexed minter, uint256 startWith, uint256 times);
          
          
          uint256 public totalSeals;
          uint256 public totalCount = 10000; //bruhTotal
          uint256 public maxBatch = 20; // bruhBatch
          uint256 public price = 60000000000000000; // 0.06 eth
          uint256 public releaseUnix = 1630447200; // works KEKW
          uint256 public presaleRelease = 1630445400; // presale time (please work)
          string public baseURI;
          bool public started;
          bool public whiteListStart;
          mapping(address => bool) whiteList;
          mapping(address => uint256) whiteListMintCount;
          uint addressRegistryCount;
          
          constructor(string memory name_, string memory symbol_, string memory baseURI_) ERC721(name_, symbol_) {
              baseURI = baseURI_;
          }
          
          modifier canWhitelistMint() {
              require(block.timestamp >= presaleRelease, "Wait buddy");
              require(whiteListStart, "Hang on boys, youll get in soon");
              require(whiteList[msg.sender] == true, "Not whitelisted.");
              _; 
          }
      
          modifier mintEnabled() {
              require(block.timestamp >= releaseUnix, "Wait buddy");
              require(started, "not started");
              _;
          }
       
          function totalSupply() public view virtual returns (uint256) {
              return totalSeals;
          }
       
          function _baseURI() internal view virtual override returns (string memory){
              return baseURI;
          }
       
          function setBaseURI(string memory _newURI) public onlyOwner {
              baseURI = _newURI;
          }
       
          function changePrice(uint256 _newPrice) public onlyOwner {
              price = _newPrice;
          }
          
          function changeStartDate(uint256 newDate) public onlyOwner {
              releaseUnix = newDate;
          }
          
          function changePresaleStartDate(uint256 newDate) public onlyOwner {
              presaleRelease = newDate;
          }
       
          function setTokenURI(uint256 _tokenId, string memory _tokenURI) public onlyOwner {
              _setTokenURI(_tokenId, _tokenURI);
          }
       
          function setNormalStart(bool _start) public onlyOwner {
              started = _start;
          }
          
          function setWhiteListStart(bool _start) public onlyOwner {
              whiteListStart = _start;
          }
          
          function getWhitelistMintAmount(address _addr) public view virtual returns (uint256) {
              return whiteListMintCount[_addr];
          }
       
          function mintSeal(uint256 _times) payable public mintEnabled {
              require(_times >0 && _times <= maxBatch, "mint wrong number");
              require(totalSeals + _times <= totalCount, "too much");
              require(msg.value == _times * price, "value error");
              payable(owner()).transfer(msg.value);
              emit MintSeals(_msgSender(), totalSeals+1, _times);
              for(uint256 i=0; i< _times; i++){
                  _mint(_msgSender(), 1 + totalSeals++);
              }
          } 
          
          function adminMint(uint256 _times) payable public onlyOwner {
              require(_times >0 && _times <= maxBatch, "mint wrong number");
              require(totalSeals + _times <= totalCount, "too much");
              require(msg.value == _times * price, "value error");
              payable(owner()).transfer(msg.value);
              emit MintSeals(_msgSender(), totalSeals+1, _times);
              for(uint256 i=0; i< _times; i++){
                  _mint(_msgSender(), 1 + totalSeals++);
              }
          }
          
          
          function whitelistMint(uint _times) payable public canWhitelistMint {
              require(whiteListMintCount[msg.sender] - _times >= 0, "Over mint limit for address.");
              require(totalSeals + _times <= totalCount, "Mint amount will exceed total collection amount.");
              require(msg.value == _times * price, "Incorrect transaction value.");
              payable(owner()).transfer(msg.value);
              whiteListMintCount[_msgSender()] -= _times;
              emit MintSeals(_msgSender(), totalSeals+1, _times);
              for(uint256 i=0; i< _times; i++){
                  _mint(_msgSender(), 1 + totalSeals++);
              }
          }
          
          
          function adminMintGiveaways(address _addr) public onlyOwner {
              require(totalSeals + 1 <= totalCount, "Mint amount will exceed total collection amount.");
              emit MintSeals(_addr, totalSeals+1, 1);
              _mint(_addr, 1 + totalSeals++);
          } 
          
          
          function addToWhitelist(address _addr, uint numberOfMints) public onlyOwner {
              whiteList[_addr] = true;
              whiteListMintCount[_addr] = numberOfMints;
              addressRegistryCount++;
          }
          
          function addToWhitelistBulk(address[] memory _addr,uint[] memory _numberOfMints) public onlyOwner {
              for(uint256 i = 0; i < _addr.length; i++){
                  whiteList[_addr[i]] = true;
                  whiteListMintCount[_addr[i]] = _numberOfMints[i];
              }
          }
          
          //Check address is in whiteList 
          function isAddressInWhitelist(address _addr) public view virtual returns(bool) {
                  return whiteList[_addr] == true;
          }
          
      }