EthereumEthereum
0x31...3cf4
Pets Oasis

Pets Oasis

PO

收藏品
大小
2,846
收藏品
所有者
418
15% 独特的所有者
此合同的源代码已经过验证!
合同元数据
编译器
0.8.26+commit.8a97fa7a
语言
Solidity
合同源代码
文件 1 的 16:ERC173.sol
// SPDX-License-Identifier: MIT

/**
 * Author: Lambdalf the White
 */
pragma solidity ^0.8.17;

import { IERC173 } from "../interfaces/IERC173.sol";

abstract contract ERC173 is IERC173 {
  // **************************************
  // *****     STORAGE VARIABLES      *****
  // **************************************
  /// @dev The current contract owner.
  address private _owner;
  // **************************************

  constructor(address owner_) {
    _owner = owner_;
  }

  // **************************************
  // *****          MODIFIERS         *****
  // **************************************
  /// @dev Throws if called by any account other than the owner.
  modifier onlyOwner() {
    if (owner() != msg.sender) {
      revert IERC173_NOT_OWNER();
    }
    _;
  }
  // **************************************

  // **************************************
  // *****       CONTRACT_OWNER       *****
  // **************************************
  /// @dev Transfers ownership of the contract to `newOwner_`.
  ///
  /// @param newOwner_ address of the new contract owner
  ///
  /// Requirements:
  ///
  /// - Caller must be the contract owner.
  function transferOwnership(address newOwner_) public virtual override onlyOwner {
    address _oldOwner_ = _owner;
    _owner = newOwner_;
    emit OwnershipTransferred(_oldOwner_, newOwner_);
  }
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
  /// @dev Returns the address of the current contract owner.
  ///
  /// @return contractOwner the current contract owner
  function owner() public view virtual override returns (address contractOwner) {
    return _owner;
  }
  // **************************************
}
合同源代码
文件 2 的 16:ERC2981.sol
// SPDX-License-Identifier: MIT

/**
 * Author: Lambdalf the White
 */
pragma solidity ^0.8.17;

import { IERC2981 } from "../interfaces/IERC2981.sol";

abstract contract ERC2981 is IERC2981 {
  // **************************************
  // *****         DATA TYPES         *****
  // **************************************
  /// @dev A structure representing royalties
  struct RoyaltyData {
    address recipient;
    uint96 rate;
  }
  // **************************************

  // **************************************
  // *****    BYTECODE  VARIABLES     *****
  // **************************************
  /// @dev Royalty rate is stored out of 10,000 instead of a percentage
  ///   to allow for up to two digits below the unit such as 2.5% or 1.25%.
  uint256 public constant ROYALTY_BASE = 10_000;
  // **************************************

  // **************************************
  // *****     STORAGE VARIABLES      *****
  // **************************************
  /// @dev Represents the royalties on each sale on secondary markets.
  ///   Set rate to 0 to have no royalties.
  RoyaltyData private _royaltyData;
  // **************************************

  constructor(address royaltyRecipient_, uint96 royaltyRate_) {
    _setRoyaltyInfo(royaltyRecipient_, royaltyRate_);
  }

  // **************************************
  // *****            VIEW            *****
  // **************************************
  /// @dev Called with the sale price to determine how much royalty is owed and to whom.
  ///
  /// @param tokenId_ identifier of the NFT being referenced
  /// @param salePrice_ the sale price of the token sold
  ///
  /// @return receiver the address receiving the royalties
  /// @return royaltyAmount the royalty payment amount
  /* solhint-disable no-unused-vars */
  function royaltyInfo(
    uint256 tokenId_,
    uint256 salePrice_
  )
    public
    view
    virtual
    override
    returns (address receiver, uint256 royaltyAmount)
  {
    RoyaltyData memory _data_ = _royaltyData;
    if (salePrice_ == 0 || _data_.rate == 0 || _data_.recipient == address(0)) {
      return (address(0), 0);
    }
    uint256 _royaltyAmount_ = _data_.rate * salePrice_ / ROYALTY_BASE;
    return (_data_.recipient, _royaltyAmount_);
  }
  /* solhint-enable no-unused-vars */
  // **************************************

  // **************************************
  // *****          INTERNAL          *****
  // **************************************
  /// @dev Sets the royalty rate to `newRoyaltyRate_` and the royalty recipient to `newRoyaltyRecipient_`.
  ///
  /// @param newRoyaltyRecipient_ the address that will receive royalty payments
  /// @param newRoyaltyRate_ the percentage of the sale price that will be taken off as royalties,
  ///   expressed in Basis Points (100 BP = 1%)
  ///
  /// Requirements:
  ///
  /// - `newRoyaltyRate_` cannot be higher than {ROYALTY_BASE};
  function _setRoyaltyInfo(address newRoyaltyRecipient_, uint96 newRoyaltyRate_) internal virtual {
    if (newRoyaltyRate_ > ROYALTY_BASE) {
      revert IERC2981_INVALID_ROYALTIES();
    }
    _royaltyData = RoyaltyData(newRoyaltyRecipient_, newRoyaltyRate_);
  }
  // **************************************
}
合同源代码
文件 3 的 16:ERC721Batch.sol
// SPDX-License-Identifier: MIT

/**
 * Author: Lambdalf the White
 */
pragma solidity ^0.8.17;

import { IERC721 } from "../../interfaces/IERC721.sol";
import { IERC721Metadata } from "../../interfaces/IERC721Metadata.sol";
import { IERC721Enumerable } from "../../interfaces/IERC721Enumerable.sol";
import { IERC721Receiver } from "../../interfaces/IERC721Receiver.sol";
import { IERC2309 } from "../../interfaces/IERC2309.sol";

/// @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard.
/// @dev This contract does not implement ERC165, unlike the ERC721 specification recommends,
///   to simplify inheritance tree. Remember to implement it in the final contract.
///   Note: this implementation has a very inefficient {balanceOf} function.
abstract contract ERC721Batch is IERC721, IERC721Metadata, IERC721Enumerable, IERC2309 {
  // **************************************
  // *****     STORAGE VARIABLES      *****
  // **************************************
  // ***********
  // * IERC721 *
  // ***********
  /// @dev Identifier of the next token to be minted
  uint256 internal _nextId = 1;
  /// @dev Token ID mapped to approved address
  mapping(uint256 => address) internal _approvals;
  /// @dev Token owner mapped to operator approvals
  mapping(address => mapping(address => bool)) internal _operatorApprovals;
  /// @dev List of owner addresses
  mapping(uint256 => address) internal _owners;
  // ***********

  // *******************
  // * IERC721Metadata *
  // *******************
  /// @dev The token's base URI.
  string internal _baseUri;
  /// @dev The name of the tokens, for token trackers.
  string internal _name;
  /// @dev The symbol of the tokens, for token trackers.
  string internal _symbol;
  // *******************
  // **************************************

  constructor(string memory name_, string memory symbol_) {
    _name = name_;
    _symbol = symbol_;
  }

  // **************************************
  // *****          MODIFIER          *****
  // **************************************
  // ***********
  // * IERC721 *
  // ***********
  /// @dev Throws if `tokenId_` doesn't exist.
  /// A token exists if it has been minted and is not owned by the zero address.
  ///
  /// @param tokenId_ identifier of the NFT being referenced
  modifier exists(uint256 tokenId_) {
    if (!_exists(tokenId_)) {
      revert IERC721_NONEXISTANT_TOKEN();
    }
    _;
  }
  // ***********
  // **************************************

  // **************************************
  // *****           PUBLIC           *****
  // **************************************
  // ***********
  // * IERC721 *
  // ***********
  /// @dev Gives permission to `to_` to transfer the token number `tokenId_` on behalf of its owner.
  /// 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.
  ///
  /// @param to_ The new approved NFT controller
  /// @param tokenId_ The NFT to approve
  ///
  /// Requirements:
  ///
  /// - The token number `tokenId_` must exist.
  /// - The caller must own the token or be an approved operator.
  /// - Must emit an {Approval} event.
  function approve(address to_, uint256 tokenId_) public virtual override {
    address _tokenOwner_ = ownerOf(tokenId_);
    if (to_ == _tokenOwner_) {
      revert IERC721_INVALID_APPROVAL();
    }
    bool _isApproved_ = isApprovedForAll(_tokenOwner_, msg.sender);
    if (msg.sender != _tokenOwner_ && !_isApproved_) {
      revert IERC721_CALLER_NOT_APPROVED();
    }
    _approvals[tokenId_] = to_;
    emit Approval(_tokenOwner_, to_, tokenId_);
  }
  /// @dev Transfers the token number `tokenId_` from `from_` to `to_`.
  ///
  /// @param from_ The current owner of the NFT
  /// @param to_ The new owner
  /// @param tokenId_ identifier of the NFT being referenced
  ///
  /// Requirements:
  ///
  /// - The token number `tokenId_` must exist.
  /// - `from_` must be the token owner.
  /// - The caller must own the token or be an approved operator.
  /// - `to_` must not be the zero address.
  /// - If `to_` is a contract, it must implement {IERC721Receiver-onERC721Received} with a return value of
  /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`,
  /// - Must emit a {Transfer} event.

  function safeTransferFrom(address from_, address to_, uint256 tokenId_) public virtual override {
    safeTransferFrom(from_, to_, tokenId_, "");
  }
  /// @dev Transfers the token number `tokenId_` from `from_` to `to_`.
  ///
  /// @param from_ The current owner of the NFT
  /// @param to_ The new owner
  /// @param tokenId_ identifier of the NFT being referenced
  /// @param data_ Additional data with no specified format, sent in call to `to_`
  ///
  /// Requirements:
  ///
  /// - The token number `tokenId_` must exist.
  /// - `from_` must be the token owner.
  /// - The caller must own the token or be an approved operator.
  /// - `to_` must not be the zero address.
  /// - If `to_` is a contract, it must implement {IERC721Receiver-onERC721Received} with a return value of
  /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`,
  /// - Must emit a {Transfer} event.

  function safeTransferFrom(address from_, address to_, uint256 tokenId_, bytes memory data_) public virtual override {
    transferFrom(from_, to_, tokenId_);
    if (!_checkOnERC721Received(from_, to_, tokenId_, data_)) {
      revert IERC721_INVALID_RECEIVER();
    }
  }
  /// @dev Allows or disallows `operator_` to manage the caller's tokens on their behalf.
  ///
  /// @param operator_ Address to add to the set of authorized operators
  /// @param approved_ True if the operator is approved, false to revoke approval
  ///
  /// Requirements:
  ///
  /// - Must emit an {ApprovalForAll} event.

  function setApprovalForAll(address operator_, bool approved_) public virtual override {
    if (operator_ == msg.sender) {
      revert IERC721_INVALID_APPROVAL();
    }
    _operatorApprovals[msg.sender][operator_] = approved_;
    emit ApprovalForAll(msg.sender, operator_, approved_);
  }
  /// @dev Transfers the token number `tokenId_` from `from_` to `to_`.
  ///
  /// @param from_ the current owner of the NFT
  /// @param to_ the new owner
  /// @param tokenId_ identifier of the NFT being referenced
  ///
  /// Requirements:
  ///
  /// - The token number `tokenId_` must exist.
  /// - `from_` must be the token owner.
  /// - The caller must own the token or be an approved operator.
  /// - `to_` must not be the zero address.
  /// - Must emit a {Transfer} event.

  function transferFrom(address from_, address to_, uint256 tokenId_) public virtual override {
    if (to_ == address(0)) {
      revert IERC721_INVALID_RECEIVER();
    }
    address _tokenOwner_ = ownerOf(tokenId_);
    if (from_ != _tokenOwner_) {
      revert IERC721_INVALID_TOKEN_OWNER();
    }
    if (!_isApprovedOrOwner(_tokenOwner_, msg.sender, tokenId_)) {
      revert IERC721_CALLER_NOT_APPROVED();
    }
    _transfer(from_, to_, tokenId_);
  }
  // ***********
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
  // ***********
  // * IERC721 *
  // ***********
  /// @dev Returns the number of tokens in `tokenOwner_`'s account.
  ///
  /// @param tokenOwner_ address that owns tokens
  ///
  /// @return ownerBalance the nomber of tokens owned by `tokenOwner_`
  ///
  /// Requirements:
  ///
  /// - `tokenOwner_` must not be the zero address
  function balanceOf(address tokenOwner_) public view virtual override returns (uint256 ownerBalance) {
    if (tokenOwner_ == address(0)) {
      revert IERC721_INVALID_TOKEN_OWNER();
    }
    address _currentTokenOwner_;
    uint256 _index_ = 1;
    while (_index_ < _nextId) {
      if (_exists(_index_)) {
        if (_owners[_index_] != address(0)) {
          _currentTokenOwner_ = _owners[_index_];
        }
        if (tokenOwner_ == _currentTokenOwner_) {
          unchecked {
            ++ownerBalance;
          }
        }
      }
      unchecked {
        ++_index_;
      }
    }
  }
  /// @dev Returns the address that has been specifically allowed to manage `tokenId_` on behalf of its owner.
  ///
  /// @param tokenId_ the NFT that has been approved
  ///
  /// @return approved the address allowed to manage `tokenId_`
  ///
  /// Requirements:
  ///
  /// - `tokenId_` must exist.
  ///
  /// Note: See {Approve}

  function getApproved(uint256 tokenId_) public view virtual override exists(tokenId_) returns (address approved) {
    return _approvals[tokenId_];
  }
  /// @dev Returns whether `operator_` is allowed to manage tokens on behalf of `tokenOwner_`.
  ///
  /// @param tokenOwner_ address that owns tokens
  /// @param operator_ address that tries to manage tokens
  ///
  /// @return isApproved whether `operator_` is allowed to handle `tokenOwner`'s tokens
  ///
  /// Note: See {setApprovalForAll}

  function isApprovedForAll(
    address tokenOwner_,
    address operator_
  )
    public
    view
    virtual
    override
    returns (bool isApproved)
  {
    return _operatorApprovals[tokenOwner_][operator_];
  }
  /// @dev Returns the owner of the token number `tokenId_`.
  ///
  /// @param tokenId_ the NFT to verify ownership of
  ///
  /// @return tokenOwner the owner of token number `tokenId_`
  ///
  /// Requirements:
  ///
  /// - `tokenId_` must exist.

  function ownerOf(uint256 tokenId_) public view virtual override exists(tokenId_) returns (address tokenOwner) {
    uint256 _tokenId_ = tokenId_;
    tokenOwner = _owners[_tokenId_];
    while (tokenOwner == address(0)) {
      unchecked {
        --_tokenId_;
      }
      tokenOwner = _owners[_tokenId_];
    }
  }
  // ***********

  // *********************
  // * IERC721Enumerable *
  // *********************
  /// @dev Enumerate valid NFTs
  ///
  /// @param index_ the index requested
  ///
  /// @return tokenId the identifier of the token at the specified index
  ///
  /// Requirements:
  ///
  /// - `index_` must be less than {totalSupply()}
  function tokenByIndex(uint256 index_) public view virtual override returns (uint256) {
    if (index_ >= _nextId - 1) {
      revert IERC721Enumerable_INDEX_OUT_OF_BOUNDS();
    }
    return index_ + 1;
  }
  /// @dev Enumerate NFTs assigned to an owner
  ///
  /// @param tokenOwner_ the address requested
  /// @param index_ the index requested
  ///
  /// @return tokenId the identifier of the token at the specified index
  ///
  /// Requirements:
  ///
  /// - `index_` must be less than {balanceOf(tokenOwner_)}
  /// - `tokenOwner_` must not be the zero address

  function tokenOfOwnerByIndex(address tokenOwner_, uint256 index_) public view virtual override returns (uint256) {
    if (tokenOwner_ == address(0)) {
      revert IERC721_INVALID_TOKEN_OWNER();
    }
    address _currentTokenOwner_;
    uint256 _index_ = 1;
    uint256 _ownerBalance_;
    while (_index_ < _nextId) {
      if (_exists(_index_)) {
        if (_owners[_index_] != address(0)) {
          _currentTokenOwner_ = _owners[_index_];
        }
        if (tokenOwner_ == _currentTokenOwner_) {
          if (index_ == _ownerBalance_) {
            return _index_;
          }
          unchecked {
            ++_ownerBalance_;
          }
        }
      }
      unchecked {
        ++_index_;
      }
    }
    revert IERC721Enumerable_OWNER_INDEX_OUT_OF_BOUNDS();
  }
  /// @notice Count NFTs tracked by this contract
  ///
  /// @return supply the number of NFTs in existence

  function totalSupply() public view virtual override returns (uint256 supply) {
    return _nextId - 1;
  }
  // *********************

  // *******************
  // * IERC721Metadata *
  // *******************
  /// @dev A descriptive name for a collection of NFTs in this contract
  ///
  /// @return tokenName The descriptive name of the NFTs
  function name() public view virtual override returns (string memory tokenName) {
    return _name;
  }
  /// @dev An abbreviated name for NFTs in this contract
  ///
  /// @return tokenSymbol The abbreviated name of the NFTs

  function symbol() public view virtual override returns (string memory tokenSymbol) {
    return _symbol;
  }
  /// @dev A distinct Uniform Resource Identifier (URI) for a given asset.
  ///
  /// @param tokenId_ the NFT that has been approved
  ///
  /// @return uri the URI of the token
  ///
  /// Requirements:
  ///
  /// - `tokenId_` must exist.

  function tokenURI(uint256 tokenId_) public view virtual override exists(tokenId_) returns (string memory uri) {
    return /*bytes(_baseUri).length > 0 ?*/ string(abi.encodePacked(_baseUri, _toString(tokenId_))); /*:
      _toString(tokenId_)*/
  }
  // *******************
  // **************************************

  // **************************************
  // *****          INTERNAL          *****
  // **************************************
  // ***********
  // * IERC721 *
  // ***********
  /// @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 owning the token being transferred
  /// @param to_ address the token is being transferred to
  /// @param tokenId_ identifier of the NFT being referenced
  /// @param data_ optional data to send along with the call
  ///
  /// @return isValidReceiver whether the call correctly returned the expected value
  function _checkOnERC721Received(
    address from_,
    address to_,
    uint256 tokenId_,
    bytes memory data_
  )
    internal
    virtual
    returns (bool isValidReceiver)
  {
    // 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.
    //
    // IMPORTANT
    // It is unsafe to assume that an address not flagged by this method
    // is an externally-owned account (EOA) and not a contract.
    //
    // Among others, the following types of addresses will not be flagged:
    //
    //  - 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
    uint256 _size_;
    assembly {
      _size_ := extcodesize(to_)
    }
    // If address is a contract, check that it is aware of how to handle ERC721 tokens
    if (_size_ > 0) {
      try IERC721Receiver(to_).onERC721Received(msg.sender, from_, tokenId_, data_) returns (bytes4 retval) {
        return retval == IERC721Receiver.onERC721Received.selector;
      } catch (bytes memory reason) {
        if (reason.length == 0) {
          revert IERC721_INVALID_RECEIVER();
        } else {
          assembly {
            revert(add(32, reason), mload(reason))
          }
        }
      }
    } else {
      return true;
    }
  }
  /// @dev Internal function returning whether a token exists.
  /// A token exists if it has been minted and is not owned by the zero address.
  ///
  /// @param tokenId_ identifier of the NFT being referenced
  ///
  /// @return tokenExist whether the token exists

  function _exists(uint256 tokenId_) internal view virtual returns (bool tokenExist) {
    if (tokenId_ == 0) {
      return false;
    }
    return tokenId_ < _nextId;
  }
  /// @dev Internal function returning whether `operator_` is allowed to handle `tokenId_`
  ///
  /// Note: To avoid multiple checks for the same data, it is assumed
  /// that existence of `tokenId_` has been verified prior via {_exists}
  /// If it hasn't been verified, this function might panic
  ///
  /// @param operator_ address that tries to handle the token
  /// @param tokenId_ identifier of the NFT being referenced
  ///
  /// @return isApproved whether `operator_` is allowed to manage the token

  function _isApprovedOrOwner(
    address tokenOwner_,
    address operator_,
    uint256 tokenId_
  )
    internal
    view
    virtual
    returns (bool isApproved)
  {
    return operator_ == tokenOwner_ || operator_ == getApproved(tokenId_) || _operatorApprovals[tokenOwner_][operator_];
  }
  /// @dev Mints `qty_` tokens and transfers them to `toAddress_`.
  ///
  /// Emits one or more {Transfer} event.
  ///
  /// @param toAddress_ address receiving the NFTs
  /// @param qty_ number of NFTs being minted

  function _mint(address toAddress_, uint256 qty_) internal virtual {
    uint256 _firstToken_ = _nextId;
    uint256 _nextStart_ = _firstToken_ + qty_;
    uint256 _lastToken_ = _nextStart_ - 1;
    _owners[_firstToken_] = toAddress_;
    if (_lastToken_ > _firstToken_) {
      _owners[_lastToken_] = toAddress_;
    }
    _nextId = _nextStart_;
    while (_firstToken_ < _nextStart_) {
      emit Transfer(address(0), toAddress_, _firstToken_);
      unchecked {
        ++_firstToken_;
      }
    }
  }
  /// @dev Mints `qty_` tokens and transfers them to `toAddress_`.
  ///
  /// Emits a {ConsecutiveTransfer} event.
  ///
  /// @param toAddress_ address receiving the NFTs
  /// @param qty_ number of NFTs being minted

  function _mint2309(address toAddress_, uint256 qty_) internal virtual {
    uint256 _firstToken_ = _nextId;
    uint256 _nextStart_ = _firstToken_ + qty_;
    uint256 _lastToken_ = _nextStart_ - 1;
    _owners[_firstToken_] = toAddress_;
    if (_lastToken_ > _firstToken_) {
      _owners[_lastToken_] = toAddress_;
    }
    _nextId = _nextStart_;
    emit ConsecutiveTransfer(_firstToken_, _lastToken_, address(0), toAddress_);
  }
  /// @dev Transfers `tokenId_` from `fromAddress_` to `toAddress_`.
  ///
  /// Emits a {Transfer} event.
  ///
  /// @param fromAddress_ the current owner of the NFT
  /// @param toAddress_ the new owner
  /// @param tokenId_ identifier of the NFT being referenced

  function _transfer(address fromAddress_, address toAddress_, uint256 tokenId_) internal virtual {
    _approvals[tokenId_] = address(0);
    uint256 _previousId_ = tokenId_ > 1 ? tokenId_ - 1 : 1;
    uint256 _nextId_ = tokenId_ + 1;
    bool _previousShouldUpdate_ =
      _previousId_ < tokenId_ && _exists(_previousId_) && _owners[_previousId_] == address(0);
    bool _nextShouldUpdate_ = _exists(_nextId_) && _owners[_nextId_] == address(0);
    if (_previousShouldUpdate_) {
      _owners[_previousId_] = fromAddress_;
    }
    if (_nextShouldUpdate_) {
      _owners[_nextId_] = fromAddress_;
    }
    _owners[tokenId_] = toAddress_;
    emit Transfer(fromAddress_, toAddress_, tokenId_);
  }
  // ***********

  // *******************
  // * IERC721Metadata *
  // *******************
  /// @notice Updates the baseUri for the tokens.
  ///
  /// @param newBaseUri_ the new baseUri for the tokens
  ///
  /// Requirements:
  ///
  /// - Caller must be the contract owner.
  function _setBaseUri(string memory newBaseUri_) internal virtual {
    _baseUri = newBaseUri_;
  }
  /// @dev Converts a `uint256` to its ASCII `string` decimal representation.
  ///
  /// @param value_ the value to convert to string.
  ///
  /// @return str the string representation of `value_`

  function _toString(uint256 value_) internal pure virtual returns (string memory str) {
    assembly {
      // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
      // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
      // We will need 1 word for the trailing zeros padding, 1 word for the length,
      // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
      let m := add(mload(0x40), 0xa0)
      // Update the free memory pointer to allocate.
      mstore(0x40, m)
      // Assign the `str` to the end.
      str := sub(m, 0x20)
      // Zeroize the slot after the string.
      mstore(str, 0)

      // Cache the end of the memory to calculate the length later.
      let end := str

      // We write the string from rightmost digit to leftmost digit.
      // The following is essentially a do-while loop that also handles the zero case.
      // prettier-ignore
      for { let temp := value_ } 1 { } {
        // solhint-disable-line
        str := sub(str, 1)
        // Write the character to the pointer.
        // The ASCII index of the '0' character is 48.
        mstore8(str, add(48, mod(temp, 10)))
        // Keep dividing `temp` until zero.
        temp := div(temp, 10)
        // prettier-ignore
        if iszero(temp) { break }
      }

      let length := sub(end, str)
      // Move the pointer 32 bytes leftwards to make room for the length.
      str := sub(str, 0x20)
      // Store the length.
      mstore(str, length)
    }
  }
  // *******************
  // **************************************
}
合同源代码
文件 4 的 16:IArrays.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.4 <0.9.0;

interface IArrays {
  // **************************************
  // *****           ERRORS           *****
  // **************************************
  /// @dev Thrown when two related arrays have different lengths.
  error ARRAY_LENGTH_MISMATCH();
  // **************************************
}
合同源代码
文件 5 的 16:IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.4 <0.9.0;

/**
 * @dev Required interface of an ERC165 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *   Note: the ERC-165 identifier for this interface is 0x01ffc9a7.
 */
interface IERC165 {
  // **************************************
  // *****            VIEW            *****
  // **************************************
  /// @dev Returns if a contract implements an interface.
  ///   Interface identification is specified in ERC-165. This function uses less than 30,000 gas.
  function supportsInterface(bytes4 interfaceId_) external view returns (bool);
  // **************************************
}
合同源代码
文件 6 的 16:IERC173.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.4 <0.9.0;

// import "./IERC165.sol";

/**
 * @dev Required interface of an ERC173 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-173[EIP].
 *   Note: the ERC-165 identifier for this interface is 0x7f5828d0.
 */
interface IERC173 { /* is IERC165 */
  // **************************************
  // *****           ERRORS           *****
  // **************************************
  /// @dev Thrown when operator is not the contract owner.
  error IERC173_NOT_OWNER();
  // **************************************

  // **************************************
  // *****           EVENTS           *****
  // **************************************
  /// @dev This emits when ownership of a contract changes.
  ///
  /// @param previousOwner the previous contract owner
  /// @param newOwner the new contract owner
  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
  // **************************************

  // **************************************
  // *****       CONTRACT_OWNER       *****
  // **************************************
  /// @dev Set the address of the new owner of the contract.
  ///   Set `newOwner_` to address(0) to renounce any ownership.
  function transferOwnership(address newOwner_) external;
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
  /// @dev Returns the address of the owner.
  function owner() external view returns (address);
  // **************************************
}
合同源代码
文件 7 的 16:IERC2309.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.4 <0.9.0;

abstract contract IERC2309 {
  /// @dev Emitted instead of {ERC721.Transfer} when several consecutive tokens are being transferred.
  /// @dev See EIP2309 https://eips.ethereum.org/EIPS/eip-2309
  ///
  /// @param fromTokenId identifier of the first token being transferred
  /// @param toTokenId identifier of the last token being transferred
  /// @param fromAddress address tokens are being transferred from
  /// @param toAddress address tokens are being transferred to
  event ConsecutiveTransfer(
    uint256 indexed fromTokenId, uint256 toTokenId, address indexed fromAddress, address indexed toAddress
  );
}
合同源代码
文件 8 的 16:IERC2981.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.4 <0.9.0;

// import "./IERC165.sol";

/**
 * @dev Required interface of an ERC2981 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-2981[EIP].
 *   Note: the ERC-165 identifier for this interface is 0x2a55205a.
 */
interface IERC2981 { /* is IERC165 */
  // **************************************
  // *****           ERRORS           *****
  // **************************************
  /// @dev Thrown when the desired royalty rate is higher than 10,000
  error IERC2981_INVALID_ROYALTIES();
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
  /// @dev Called with the sale price to determine how much royalty is owed and to whom.
  function royaltyInfo(
    uint256 tokenId_,
    uint256 salePrice_
  )
    external
    view
    returns (address receiver, uint256 royaltyAmount);
  // **************************************
}
合同源代码
文件 9 的 16:IERC721.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.4 <0.9.0;

// import "./IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-721[EIP].
 *   Note: the ERC-165 identifier for this interface is 0x80ac58cd.
 */
interface IERC721 { /* is IERC165 */
  // **************************************
  // *****           ERRORS           *****
  // **************************************
  /// @dev Thrown when operator is not allowed to manage tokenId.
  error IERC721_CALLER_NOT_APPROVED();
  /// @dev Thrown when user tries to approve themselves for managing a token they own.
  error IERC721_INVALID_APPROVAL();
  /// @dev Thrown when a token is being transferred to a contract unable to handle it or the zero address.
  error IERC721_INVALID_RECEIVER();
  /// @dev Thrown when checking ownership of the wrong token owner.
  error IERC721_INVALID_TOKEN_OWNER();
  /// @dev Thrown when the requested token does not exist.
  error IERC721_NONEXISTANT_TOKEN();
  // **************************************

  // **************************************
  // *****           EVENTS           *****
  // **************************************
  /// @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.
  ///
  /// @param owner address that owns the token
  /// @param approved address that is allowed to manage the token
  /// @param tokenId identifier of the token being approved
  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.
  ///
  /// @param owner address that owns the tokens
  /// @param operator address that is allowed or not to manage the tokens
  /// @param approved whether the operator is allowed or not
  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
  /// @dev This 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.
  ///
  /// @param from address the token is being transferred from
  /// @param to address the token is being transferred to
  /// @param tokenId identifier of the token being transferred
  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
  // **************************************

  // **************************************
  // *****           PUBLIC           *****
  // **************************************
  /// @notice Change or reaffirm the approved address for an NFT
  /// @dev 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.
  function approve(address approved_, uint256 tokenId_) external;
  /// @notice Transfers the ownership of an NFT from one address to another address
  /// @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.
  ///   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,address,uint256,bytes)"))`.
  function safeTransferFrom(address from_, address to_, uint256 tokenId_, bytes calldata data_) external;
  /// @notice Transfers the ownership of an NFT from one address to another address
  /// @dev This works identically to the other function with an extra data parameter,
  ///   except this function just sets data to "".
  function safeTransferFrom(address from_, address to_, uint256 tokenId_) external;
  /// @notice Enable or disable approval for a third party ("operator") to manage all of `msg.sender`'s assets.
  /// @dev Emits the ApprovalForAll event. The contract MUST allow multiple operators per owner.
  function setApprovalForAll(address operator_, bool approved_) external;
  /// @notice Transfer ownership of an NFT.
  ///   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.
  function transferFrom(address from_, address to_, uint256 tokenId_) external;
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
  /// @notice Count all NFTs assigned to an owner
  /// @dev NFTs assigned to the zero address are considered invalid. Throws for queries about the zero address.
  function balanceOf(address owner_) external view returns (uint256);
  /// @notice Get the approved address for a single NFT
  /// @dev Throws if `tokenId_` is not a valid NFT.
  function getApproved(uint256 tokenId_) external view returns (address);
  /// @notice Query if an address is an authorized operator for another address
  function isApprovedForAll(address owner_, address operator_) external view returns (bool);
  /// @notice Find the owner of an NFT
  /// @dev NFTs assigned to zero address are considered invalid, and queries
  ///  about them do throw.
  function ownerOf(uint256 tokenId_) external view returns (address);
  // **************************************
}
合同源代码
文件 10 的 16:IERC721Enumerable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.4 <0.9.0;

// import "./IERC721.sol";

/**
 * @dev Required interface of an ERC721 compliant contract, optional enumeration extension, as defined in the
 * https://eips.ethereum.org/EIPS/eip-721[EIP].
 *   Note: the ERC-165 identifier for this interface is 0x780e9d63.
 */
interface IERC721Enumerable { /* is IERC721 */
  // **************************************
  // *****           ERRORS           *****
  // **************************************
  /// @dev Thrown when trying to get the token at an index that does not exist.
  error IERC721Enumerable_INDEX_OUT_OF_BOUNDS();
  /// @dev Thrown when trying to get the token owned by tokenOwner at an index that does not exist.
  error IERC721Enumerable_OWNER_INDEX_OUT_OF_BOUNDS();
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
  /// @dev Enumerate valid NFTs
  ///   Throws if `index_` >= {totalSupply()}.
  function tokenByIndex(uint256 index_) external view returns (uint256);
  /// @dev Enumerate NFTs assigned to an owner
  ///   Throws if `index_` >= {balanceOf(owner_)} or if `owner_` is the zero address, representing invalid NFTs.
  function tokenOfOwnerByIndex(address owner_, uint256 index_) external view returns (uint256);
  /// @dev Count NFTs tracked by this contract
  function totalSupply() external view returns (uint256);
  // **************************************
}
合同源代码
文件 11 的 16:IERC721Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.4 <0.9.0;

// import "./IERC721.sol";

/**
 * @dev Required interface of an ERC721 compliant contract, optional metadata extension, as defined in the
 * https://eips.ethereum.org/EIPS/eip-721[EIP].
 *   Note: the ERC-165 identifier for this interface is 0x5b5e139f.
 */
interface IERC721Metadata { /* is IERC721 */
  // **************************************
  // *****            VIEW            *****
  // **************************************
  /// @dev A descriptive name for a collection of NFTs in this contract
  function name() external view returns (string memory);
  /// @dev An abbreviated name for NFTs in this contract
  function symbol() external view returns (string memory);
  /// @dev A distinct Uniform Resource Identifier (URI) for a given asset.
  ///   Throws if `tokenId_` is not a valid NFT. URIs are defined in RFC 3986.
  ///   The URI may point to a JSON file that conforms to the "ERC721 Metadata JSON Schema".
  function tokenURI(uint256 tokenId_) external view returns (string memory);
  // **************************************
}
合同源代码
文件 12 的 16:IERC721Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.4 <0.9.0;

/**
 * @dev Required interface of an ERC721 receiver compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-721[EIP].
 *   Note: the ERC-165 identifier for this interface is 0x150b7a02.
 */
interface IERC721Receiver {
  // **************************************
  // *****            VIEW            *****
  // **************************************
  /// @dev Handle the receipt of an 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.
  ///   Note: the contract address is always the message sender.
  function onERC721Received(
    address operator_,
    address from_,
    uint256 tokenId_,
    bytes calldata data_
  )
    external
    returns (bytes4);
  // **************************************
}
合同源代码
文件 13 的 16:ITemplate.sol
// SPDX-License-Identifier: MIT

/**
 * Team: Asteria Labs
 * Author: Lambdalf the White
 */
pragma solidity >=0.8.4 <0.9.0;

interface ITemplate {
  // **************************************
  // *****           ERRORS           *****
  // **************************************
  /// @dev Thrown when a function is called with the wrong contract state.
  error CONTRACT_STATE_INCORRECT();
  /// @dev Thrown when trying to set the contract state to an invalid value.
  error CONTRACT_STATE_INVALID();
  /// @dev Thrown when an incorrect amount of eth is being sent for a payable operation.
  error ETHER_INCORRECT_PRICE();
  /// @dev Thrown when trying to withdraw from the contract with no balance.
  error ETHER_NO_BALANCE();
  /// @dev Thrown when contract fails to send ether to recipient.
  error ETHER_TRANSFER_FAIL();
  /// @dev Thrown when trying to mint 0 token.
  error NFT_INVALID_QTY();
  /// @dev Thrown when trying to set reserve to an invalid amount.
  error NFT_INVALID_RESERVE();
  /// @dev Thrown when trying to set max supply to an invalid amount.
  error NFT_INVALID_SUPPLY();
  /// @dev Thrown when trying to mint more tokens than the max allowed per transaction.
  error NFT_MAX_BATCH();
  /// @dev Thrown when trying to mint more tokens from the reserve than the amount left.
  error NFT_MAX_RESERVE();
  /// @dev Thrown when trying to mint more tokens than the amount left to be minted (except reserve).
  error NFT_MINTED_OUT();
  /// @dev Thrown when trying to call a non existant function.
  error UNKNOWN();
  // **************************************

  // **************************************
  // *****           EVENTS           *****
  // **************************************
  /// @dev Emitted when the sale state changes
  ///
  /// @param previousState the previous state of the contract
  /// @param newState the new state of the contract
  event ContractStateChanged(uint8 indexed previousState, uint8 indexed newState);
  // **************************************
}
合同源代码
文件 14 的 16:IWhitelist.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.4 <0.9.0;

interface IWhitelist {
  // **************************************
  // *****         DATA TYPES         *****
  // **************************************
  /// @dev A structure representing a signature proof to be decoded by the contract
  struct Proof {
    bytes32 r;
    bytes32 s;
    uint8 v;
  }
  // **************************************

  // **************************************
  // *****           ERRORS           *****
  // **************************************
  /// @dev Thrown when trying to query the whitelist while it's not set
  error WHITELIST_NOT_SET();
  /// @dev Thrown when operator does not have enough alloted access to fulfil their query
  error WHITELIST_FORBIDDEN();
  // **************************************
}
合同源代码
文件 15 的 16:PetsOasis.sol
// SPDX-License-Identifier: MIT

/**
 * Author: Lambdalf the White
 */
pragma solidity >=0.8.4 <0.9.0;

import { IERC721 } from "@lambdalf-dev/interfaces/IERC721.sol";
import { IERC721Enumerable } from "@lambdalf-dev/interfaces/IERC721Enumerable.sol";
import { IERC721Metadata } from "@lambdalf-dev/interfaces/IERC721Metadata.sol";
import { IERC173 } from "@lambdalf-dev/interfaces/IERC173.sol";
import { IERC165 } from "@lambdalf-dev/interfaces/IERC165.sol";
import { IERC2981 } from "@lambdalf-dev/interfaces/IERC2981.sol";
import { ITemplate } from "@lambdalf-dev/interfaces/ITemplate.sol";
import { IArrays } from "@lambdalf-dev/interfaces/IArrays.sol";
import { ERC721Batch } from "@lambdalf-dev/tokens/ERC721/ERC721Batch.sol";
import { ERC173 } from "@lambdalf-dev/utils/ERC173.sol";
import { ERC2981 } from "@lambdalf-dev/utils/ERC2981.sol";
import { Whitelist } from "@lambdalf-dev/utils/Whitelist.sol";

contract PetsOasis is IERC165, ERC721Batch, ERC173, ERC2981, ITemplate, IArrays {
  // **************************************
  // *****     STORAGE VARIABLES      *****
  // **************************************
  /// @dev Amount of NFTs reserved for team mint.
  uint256 public reserve;
  // **************************************

  // solhint-disable-next-line func-name-mixedcase
  constructor(
    uint256 reserve_,
    uint96 royaltyRate_,
    address royaltyRecipient_
  )
    ERC721Batch("Pets Oasis", "PO")
    ERC173(msg.sender)
    ERC2981(royaltyRecipient_, royaltyRate_)
  {
    reserve = reserve_;
    _setBaseUri("https://bafybeieenefkopvpnt2fvty5b4go2pyoqbkdspgnbflg44rlqpb2wua2zq.ipfs.nftstorage.link/");
  }

  // **************************************
  // *****       CONTRACT_OWNER       *****
  // **************************************
  /// @notice Mints `amounts_` tokens and transfers them to `accounts_`.
  ///
  /// @param accounts_ the list of accounts that will receive airdropped tokens
  /// @param amounts_ the amount of tokens each account will receive
  ///
  /// Requirements:
  ///
  /// - Caller must be the contract owner.
  /// - `accounts_` and `amounts_` must have the same length.
  /// - There must be enough tokens left in the reserve.
  function airdrop(address[] memory accounts_, uint256[] memory amounts_) public onlyOwner {
    uint256 _len_ = accounts_.length;
    if (_len_ != amounts_.length) {
      revert ARRAY_LENGTH_MISMATCH();
    }
    uint256 _count_;
    uint256 _totalQty_;
    while (_count_ < _len_) {
      _totalQty_ += amounts_[_count_];
      _mint(accounts_[_count_], amounts_[_count_]);
      unchecked {
        ++_count_;
      }
    }
    if (_totalQty_ > reserve) {
      revert NFT_MAX_RESERVE();
    }
    unchecked {
      reserve -= _totalQty_;
    }
  }

  // *******************
  // * IERC721Metadata *
  // *******************
  /// @notice Updates the baseUri for the tokens.
  ///
  /// @param newBaseUri_ the new baseUri for the tokens
  ///
  /// Requirements:
  ///
  /// - Caller must be the contract owner.
  function setBaseUri(string memory newBaseUri_) public onlyOwner {
    _setBaseUri(newBaseUri_);
  }
  // *******************

  // ************
  // * IERC2981 *
  // ************
  /// @dev Sets the royalty rate to `newRoyaltyRate_` and the royalty recipient to `newRoyaltyRecipient_`.
  ///
  /// @param newRoyaltyRecipient_ the address that will receive royalty payments
  /// @param newRoyaltyRate_ the percentage of the sale price that will be taken off as royalties,
  ///   expressed in Basis Points (100 BP = 1%)
  ///
  /// Requirements:
  ///
  /// - Caller must be the contract owner.
  /// - `newRoyaltyRate_` cannot be higher than {ROYALTY_BASE};
  function setRoyaltyInfo(address newRoyaltyRecipient_, uint96 newRoyaltyRate_) public onlyOwner {
    _setRoyaltyInfo(newRoyaltyRecipient_, newRoyaltyRate_);
  }
  // ************
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
  // *******************
  // * IERC721Metadata *
  // *******************
  /// @dev A distinct Uniform Resource Identifier (URI) for a given asset.
  ///
  /// @param tokenId_ the NFT that has been approved
  ///
  /// @return uri the URI of the token
  ///
  /// Requirements:
  ///
  /// - `tokenId_` must exist.

  function tokenURI(uint256 tokenId_) public view virtual override exists(tokenId_) returns (string memory uri) {
    return string(abi.encodePacked(_baseUri, _toString(tokenId_), ".png"));
  }
  // *******************

  // ***********
  // * IERC165 *
  // ***********
  /// @dev Query if a contract implements an interface.
  /// @dev see https://eips.ethereum.org/EIPS/eip-165
  ///
  /// @param interfaceId_ the interface identifier, as specified in ERC-165
  ///
  /// @return bool true if the contract implements the specified interface, false otherwise
  ///
  /// Requirements:
  ///
  /// - This function must use less than 30,000 gas.
  function supportsInterface(bytes4 interfaceId_) public pure override returns (bool) {
    return interfaceId_ == type(IERC721).interfaceId || interfaceId_ == type(IERC721Enumerable).interfaceId
      || interfaceId_ == type(IERC721Metadata).interfaceId || interfaceId_ == type(IERC173).interfaceId
      || interfaceId_ == type(IERC165).interfaceId || interfaceId_ == type(IERC2981).interfaceId;
  }
  // ***********
  // **************************************
}
合同源代码
文件 16 的 16:Whitelist.sol
// SPDX-License-Identifier: MIT

/**
 * Author: Lambdalf the White
 */
pragma solidity ^0.8.17;

import { IWhitelist } from "../interfaces/IWhitelist.sol";

abstract contract Whitelist is IWhitelist {
  // **************************************
  // *****     STORAGE VARIABLES      *****
  // **************************************
  /// @dev The address signing the whitelist proofs.
  address private _adminSigner;
  /// @dev Whitelist ID mapped to user's whitelist concumption.
  mapping(uint8 => mapping(address => uint256)) private _consumed;
  // **************************************

  // **************************************
  // *****            VIEW            *****
  // **************************************
  /// @dev Returns the amount that `account_` is allowed to access from the whitelist.
  ///
  /// @param account_ the address to validate access
  /// @param whitelistId_ the identifier of the whitelist being queried
  /// @param alloted_ the max amount of whitelist spots allocated
  /// @param proof_ the signature proof to validate whitelist allocation
  ///
  /// @return remainingAllocation the total amount of whitelist allocation remaining for `account_`
  ///
  /// Requirements:
  ///
  /// - `_adminSigner` must be set.
  function checkWhitelistAllowance(
    address account_,
    uint8 whitelistId_,
    uint256 alloted_,
    Proof memory proof_
  )
    public
    view
    virtual
    returns (uint256 remainingAllocation)
  {
    if (_adminSigner == address(0)) {
      revert WHITELIST_NOT_SET();
    }
    if (!_validateProof(account_, whitelistId_, alloted_, proof_)) {
      return 0;
    }
    return alloted_ - _consumed[whitelistId_][account_];
  }
  // **************************************

  // **************************************
  // *****          INTERNAL          *****
  // **************************************
  /// @dev Consumes `amount_` whitelist access passes from `account_`.
  ///
  /// Note: Before calling this function, eligibility should be checked through {checkWhitelistAllowance}.
  ///
  /// @param account_ the address to consume access from
  /// @param whitelistId_ the identifier of the whitelist being queried
  /// @param qty_ the amount of whitelist access consumed
  function _consumeWhitelist(address account_, uint8 whitelistId_, uint256 qty_) internal virtual {
    unchecked {
      _consumed[whitelistId_][account_] += qty_;
    }
  }
  /// @notice Updates the whitelist signer.
  ///
  /// @param newAdminSigner_ the new whitelist signer
  ///
  /// Requirements:

  function _setWhitelist(address newAdminSigner_) internal virtual {
    _adminSigner = newAdminSigner_;
  }
  /// @dev Internal function to decode a signature and compare it with the `_adminSigner`.
  ///
  /// @param account_ the address to validate access
  /// @param whitelistId_ the identifier of the whitelist being queried
  /// @param alloted_ the max amount of whitelist spots allocated
  /// @param proof_ the signature proof to validate whitelist allocation
  ///
  /// @return isValid whether the signature is valid or not

  function _validateProof(
    address account_,
    uint8 whitelistId_,
    uint256 alloted_,
    Proof memory proof_
  )
    internal
    view
    virtual
    returns (bool isValid)
  {
    bytes32 _digest_ = keccak256(abi.encode(block.chainid, whitelistId_, alloted_, account_));
    address _signer_ = ecrecover(_digest_, proof_.v, proof_.r, proof_.s);
    return _signer_ == _adminSigner;
  }
  // **************************************
}
设置
{
  "compilationTarget": {
    "src/PetsOasis.sol": "PetsOasis"
  },
  "evmVersion": "cancun",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "none"
  },
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "remappings": [
    ":@lambdalf-dev/=node_modules/@lambdalf-dev/ethereum-contracts/src/",
    ":ds-test/=node_modules/ds-test/src/",
    ":forge-std/=node_modules/forge-std/src/",
    ":solady/=node_modules/solady/"
  ]
}
ABI
[{"inputs":[{"internalType":"uint256","name":"reserve_","type":"uint256"},{"internalType":"uint96","name":"royaltyRate_","type":"uint96"},{"internalType":"address","name":"royaltyRecipient_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ARRAY_LENGTH_MISMATCH","type":"error"},{"inputs":[],"name":"CONTRACT_STATE_INCORRECT","type":"error"},{"inputs":[],"name":"CONTRACT_STATE_INVALID","type":"error"},{"inputs":[],"name":"ETHER_INCORRECT_PRICE","type":"error"},{"inputs":[],"name":"ETHER_NO_BALANCE","type":"error"},{"inputs":[],"name":"ETHER_TRANSFER_FAIL","type":"error"},{"inputs":[],"name":"IERC173_NOT_OWNER","type":"error"},{"inputs":[],"name":"IERC2981_INVALID_ROYALTIES","type":"error"},{"inputs":[],"name":"IERC721Enumerable_INDEX_OUT_OF_BOUNDS","type":"error"},{"inputs":[],"name":"IERC721Enumerable_OWNER_INDEX_OUT_OF_BOUNDS","type":"error"},{"inputs":[],"name":"IERC721_CALLER_NOT_APPROVED","type":"error"},{"inputs":[],"name":"IERC721_INVALID_APPROVAL","type":"error"},{"inputs":[],"name":"IERC721_INVALID_RECEIVER","type":"error"},{"inputs":[],"name":"IERC721_INVALID_TOKEN_OWNER","type":"error"},{"inputs":[],"name":"IERC721_NONEXISTANT_TOKEN","type":"error"},{"inputs":[],"name":"NFT_INVALID_QTY","type":"error"},{"inputs":[],"name":"NFT_INVALID_RESERVE","type":"error"},{"inputs":[],"name":"NFT_INVALID_SUPPLY","type":"error"},{"inputs":[],"name":"NFT_MAX_BATCH","type":"error"},{"inputs":[],"name":"NFT_MAX_RESERVE","type":"error"},{"inputs":[],"name":"NFT_MINTED_OUT","type":"error"},{"inputs":[],"name":"UNKNOWN","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"fromAddress","type":"address"},{"indexed":true,"internalType":"address","name":"toAddress","type":"address"}],"name":"ConsecutiveTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"previousState","type":"uint8"},{"indexed":true,"internalType":"uint8","name":"newState","type":"uint8"}],"name":"ContractStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"ROYALTY_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts_","type":"address[]"},{"internalType":"uint256[]","name":"amounts_","type":"uint256[]"}],"name":"airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenOwner_","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"ownerBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"approved","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenOwner_","type":"address"},{"internalType":"address","name":"operator_","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"isApproved","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"tokenName","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"contractOwner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"tokenOwner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"uint256","name":"salePrice_","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator_","type":"address"},{"internalType":"bool","name":"approved_","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newBaseUri_","type":"string"}],"name":"setBaseUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRoyaltyRecipient_","type":"address"},{"internalType":"uint96","name":"newRoyaltyRate_","type":"uint96"}],"name":"setRoyaltyInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId_","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"tokenSymbol","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenOwner_","type":"address"},{"internalType":"uint256","name":"index_","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"supply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from_","type":"address"},{"internalType":"address","name":"to_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner_","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]