// 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;
}
// **************************************
}
// 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_);
}
// **************************************
}
// 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)
}
}
// *******************
// **************************************
}
// 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();
// **************************************
}
// 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);
// **************************************
}
// 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);
// **************************************
}
// 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
);
}
// 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);
// **************************************
}
// 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);
// **************************************
}
// 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);
// **************************************
}
// 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);
// **************************************
}
// 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);
// **************************************
}
// 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);
// **************************************
}
// 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();
// **************************************
}
// 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;
}
// ***********
// **************************************
}
// 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/"
]
}
[{"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"}]