// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
address constant CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS = 0x000000000000AAeB6D7670E522A718067333cd4E;
address constant CANONICAL_CORI_SUBSCRIPTION = 0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6;
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {OperatorFilterer} from "./OperatorFilterer.sol";
import {CANONICAL_CORI_SUBSCRIPTION} from "./lib/Constants.sol";
/**
* @title DefaultOperatorFilterer
* @notice Inherits from OperatorFilterer and automatically subscribes to the default OpenSea subscription.
* @dev Please note that if your token contract does not provide an owner with EIP-173, it must provide
* administration methods on the contract itself to interact with the registry otherwise the subscription
* will be locked to the options set during construction.
*/
abstract contract DefaultOperatorFilterer is OperatorFilterer {
/// @dev The constructor that is called when the contract is being deployed.
constructor() OperatorFilterer(CANONICAL_CORI_SUBSCRIPTION, true) {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC2981 NFT Royalty Standard implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC2981.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/common/ERC2981.sol)
abstract contract ERC2981 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The royalty fee numerator exceeds the fee denominator.
error RoyaltyOverflow();
/// @dev The royalty receiver cannot be the zero address.
error RoyaltyReceiverIsZeroAddress();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The default royalty info is given by:
/// ```
/// let packed := sload(_ERC2981_MASTER_SLOT_SEED)
/// let receiver := shr(96, packed)
/// let royaltyFraction := xor(packed, shl(96, receiver))
/// ```
///
/// The per token royalty info is given by.
/// ```
/// mstore(0x00, tokenId)
/// mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
/// let packed := sload(keccak256(0x00, 0x40))
/// let receiver := shr(96, packed)
/// let royaltyFraction := xor(packed, shl(96, receiver))
/// ```
uint256 private constant _ERC2981_MASTER_SLOT_SEED = 0xaa4ec00224afccfdb7;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC2981 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Checks that `_feeDenominator` is non-zero.
constructor() {
require(_feeDenominator() != 0, "Fee denominator cannot be zero.");
}
/// @dev Returns the denominator for the royalty amount.
/// Defaults to 10000, which represents fees in basis points.
/// Override this function to return a custom amount if needed.
function _feeDenominator() internal pure virtual returns (uint96) {
return 10000;
}
/// @dev Returns true if this contract implements the interface defined by `interfaceId`.
/// See: https://eips.ethereum.org/EIPS/eip-165
/// This function call must use less than 30000 gas.
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let s := shr(224, interfaceId)
// ERC165: 0x01ffc9a7, ERC2981: 0x2a55205a.
result := or(eq(s, 0x01ffc9a7), eq(s, 0x2a55205a))
}
}
/// @dev Returns the `receiver` and `royaltyAmount` for `tokenId` sold at `salePrice`.
function royaltyInfo(uint256 tokenId, uint256 salePrice)
public
view
virtual
returns (address receiver, uint256 royaltyAmount)
{
uint256 feeDenominator = _feeDenominator();
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, tokenId)
mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
let packed := sload(keccak256(0x00, 0x40))
receiver := shr(96, packed)
if iszero(receiver) {
packed := sload(mload(0x20))
receiver := shr(96, packed)
}
let x := salePrice
let y := xor(packed, shl(96, receiver)) // `feeNumerator`.
// Overflow check, equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
// Out-of-gas revert. Should not be triggered in practice, but included for safety.
returndatacopy(returndatasize(), returndatasize(), mul(y, gt(x, div(not(0), y))))
royaltyAmount := div(mul(x, y), feeDenominator)
}
}
/// @dev Sets the default royalty `receiver` and `feeNumerator`.
///
/// Requirements:
/// - `receiver` must not be the zero address.
/// - `feeNumerator` must not be greater than the fee denominator.
function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
uint256 feeDenominator = _feeDenominator();
/// @solidity memory-safe-assembly
assembly {
feeNumerator := shr(160, shl(160, feeNumerator))
if gt(feeNumerator, feeDenominator) {
mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`.
revert(0x1c, 0x04)
}
let packed := shl(96, receiver)
if iszero(packed) {
mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`.
revert(0x1c, 0x04)
}
sstore(_ERC2981_MASTER_SLOT_SEED, or(packed, feeNumerator))
}
}
/// @dev Sets the default royalty `receiver` and `feeNumerator` to zero.
function _deleteDefaultRoyalty() internal virtual {
/// @solidity memory-safe-assembly
assembly {
sstore(_ERC2981_MASTER_SLOT_SEED, 0)
}
}
/// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId`.
///
/// Requirements:
/// - `receiver` must not be the zero address.
/// - `feeNumerator` must not be greater than the fee denominator.
function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator)
internal
virtual
{
uint256 feeDenominator = _feeDenominator();
/// @solidity memory-safe-assembly
assembly {
feeNumerator := shr(160, shl(160, feeNumerator))
if gt(feeNumerator, feeDenominator) {
mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`.
revert(0x1c, 0x04)
}
let packed := shl(96, receiver)
if iszero(packed) {
mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`.
revert(0x1c, 0x04)
}
mstore(0x00, tokenId)
mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
sstore(keccak256(0x00, 0x40), or(packed, feeNumerator))
}
}
/// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId` to zero.
function _resetTokenRoyalty(uint256 tokenId) internal virtual {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, tokenId)
mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
sstore(keccak256(0x00, 0x40), 0)
}
}
}
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs
pragma solidity ^0.8.4;
import './IERC721A.sol';
/**
* @dev Interface of ERC721 token receiver.
*/
interface ERC721A__IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
/**
* @title ERC721A
*
* @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
* Non-Fungible Token Standard, including the Metadata extension.
* Optimized for lower gas during batch mints.
*
* Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
* starting from `_startTokenId()`.
*
* Assumptions:
*
* - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
* - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
*/
contract ERC721A is IERC721A {
// Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
struct TokenApprovalRef {
address value;
}
// =============================================================
// CONSTANTS
// =============================================================
// Mask of an entry in packed address data.
uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
// The bit position of `numberMinted` in packed address data.
uint256 private constant _BITPOS_NUMBER_MINTED = 64;
// The bit position of `numberBurned` in packed address data.
uint256 private constant _BITPOS_NUMBER_BURNED = 128;
// The bit position of `aux` in packed address data.
uint256 private constant _BITPOS_AUX = 192;
// Mask of all 256 bits in packed address data except the 64 bits for `aux`.
uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
// The bit position of `startTimestamp` in packed ownership.
uint256 private constant _BITPOS_START_TIMESTAMP = 160;
// The bit mask of the `burned` bit in packed ownership.
uint256 private constant _BITMASK_BURNED = 1 << 224;
// The bit position of the `nextInitialized` bit in packed ownership.
uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;
// The bit mask of the `nextInitialized` bit in packed ownership.
uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;
// The bit position of `extraData` in packed ownership.
uint256 private constant _BITPOS_EXTRA_DATA = 232;
// Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
// The mask of the lower 160 bits for addresses.
uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
// The maximum `quantity` that can be minted with {_mintERC2309}.
// This limit is to prevent overflows on the address data entries.
// For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
// is required to cause an overflow, which is unrealistic.
uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
// The `Transfer` event signature is given by:
// `keccak256(bytes("Transfer(address,address,uint256)"))`.
bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
// =============================================================
// STORAGE
// =============================================================
// The next token ID to be minted.
uint256 private _currentIndex;
// The number of tokens burned.
uint256 private _burnCounter;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Mapping from token ID to ownership details
// An empty struct value does not necessarily mean the token is unowned.
// See {_packedOwnershipOf} implementation for details.
//
// Bits Layout:
// - [0..159] `addr`
// - [160..223] `startTimestamp`
// - [224] `burned`
// - [225] `nextInitialized`
// - [232..255] `extraData`
mapping(uint256 => uint256) private _packedOwnerships;
// Mapping owner address to address data.
//
// Bits Layout:
// - [0..63] `balance`
// - [64..127] `numberMinted`
// - [128..191] `numberBurned`
// - [192..255] `aux`
mapping(address => uint256) private _packedAddressData;
// Mapping from token ID to approved address.
mapping(uint256 => TokenApprovalRef) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
// =============================================================
// TOKEN COUNTING OPERATIONS
// =============================================================
/**
* @dev Returns the starting token ID.
* To change the starting token ID, please override this function.
*/
function _startTokenId() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev Returns the next token ID to be minted.
*/
function _nextTokenId() internal view virtual returns (uint256) {
return _currentIndex;
}
/**
* @dev Returns the total number of tokens in existence.
* Burned tokens will reduce the count.
* To get the total number of tokens minted, please see {_totalMinted}.
*/
function totalSupply() public view virtual override returns (uint256) {
// Counter underflow is impossible as _burnCounter cannot be incremented
// more than `_currentIndex - _startTokenId()` times.
unchecked {
return _currentIndex - _burnCounter - _startTokenId();
}
}
/**
* @dev Returns the total amount of tokens minted in the contract.
*/
function _totalMinted() internal view virtual returns (uint256) {
// Counter underflow is impossible as `_currentIndex` does not decrement,
// and it is initialized to `_startTokenId()`.
unchecked {
return _currentIndex - _startTokenId();
}
}
/**
* @dev Returns the total number of tokens burned.
*/
function _totalBurned() internal view virtual returns (uint256) {
return _burnCounter;
}
// =============================================================
// ADDRESS DATA OPERATIONS
// =============================================================
/**
* @dev Returns the number of tokens in `owner`'s account.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
if (owner == address(0)) _revert(BalanceQueryForZeroAddress.selector);
return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the number of tokens minted by `owner`.
*/
function _numberMinted(address owner) internal view returns (uint256) {
return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the number of tokens burned by or on behalf of `owner`.
*/
function _numberBurned(address owner) internal view returns (uint256) {
return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
*/
function _getAux(address owner) internal view returns (uint64) {
return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
}
/**
* Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
* If there are multiple variables, please pack them into a uint64.
*/
function _setAux(address owner, uint64 aux) internal virtual {
uint256 packed = _packedAddressData[owner];
uint256 auxCasted;
// Cast `aux` with assembly to avoid redundant masking.
/// @solidity memory-safe-assembly
assembly {
auxCasted := aux
}
packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
_packedAddressData[owner] = packed;
}
// =============================================================
// IERC165
// =============================================================
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
// The interface IDs are constants representing the first 4 bytes
// of the XOR of all function selectors in the interface.
// See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
// (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
return
interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
}
// =============================================================
// IERC721Metadata
// =============================================================
/**
* @dev Returns the token collection name.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the token collection symbol.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
if (!_exists(tokenId)) _revert(URIQueryForNonexistentToken.selector);
string memory baseURI = _baseURI();
return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, it can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return '';
}
// =============================================================
// OWNERSHIPS OPERATIONS
// =============================================================
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
return address(uint160(_packedOwnershipOf(tokenId)));
}
/**
* @dev Gas spent here starts off proportional to the maximum mint batch size.
* It gradually moves to O(1) as tokens get transferred around over time.
*/
function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
return _unpackedOwnership(_packedOwnershipOf(tokenId));
}
/**
* @dev Returns the unpacked `TokenOwnership` struct at `index`.
*/
function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
return _unpackedOwnership(_packedOwnerships[index]);
}
/**
* @dev Returns whether the ownership slot at `index` is initialized.
* An uninitialized slot does not necessarily mean that the slot has no owner.
*/
function _ownershipIsInitialized(uint256 index) internal view virtual returns (bool) {
return _packedOwnerships[index] != 0;
}
/**
* @dev Initializes the ownership slot minted at `index` for efficiency purposes.
*/
function _initializeOwnershipAt(uint256 index) internal virtual {
if (_packedOwnerships[index] == 0) {
_packedOwnerships[index] = _packedOwnershipOf(index);
}
}
/**
* Returns the packed ownership data of `tokenId`.
*/
function _packedOwnershipOf(uint256 tokenId) private view returns (uint256 packed) {
if (_startTokenId() <= tokenId) {
packed = _packedOwnerships[tokenId];
// If the data at the starting slot does not exist, start the scan.
if (packed == 0) {
if (tokenId >= _currentIndex) _revert(OwnerQueryForNonexistentToken.selector);
// Invariant:
// There will always be an initialized ownership slot
// (i.e. `ownership.addr != address(0) && ownership.burned == false`)
// before an unintialized ownership slot
// (i.e. `ownership.addr == address(0) && ownership.burned == false`)
// Hence, `tokenId` will not underflow.
//
// We can directly compare the packed value.
// If the address is zero, packed will be zero.
for (;;) {
unchecked {
packed = _packedOwnerships[--tokenId];
}
if (packed == 0) continue;
if (packed & _BITMASK_BURNED == 0) return packed;
// Otherwise, the token is burned, and we must revert.
// This handles the case of batch burned tokens, where only the burned bit
// of the starting slot is set, and remaining slots are left uninitialized.
_revert(OwnerQueryForNonexistentToken.selector);
}
}
// Otherwise, the data exists and we can skip the scan.
// This is possible because we have already achieved the target condition.
// This saves 2143 gas on transfers of initialized tokens.
// If the token is not burned, return `packed`. Otherwise, revert.
if (packed & _BITMASK_BURNED == 0) return packed;
}
_revert(OwnerQueryForNonexistentToken.selector);
}
/**
* @dev Returns the unpacked `TokenOwnership` struct from `packed`.
*/
function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
ownership.addr = address(uint160(packed));
ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
ownership.burned = packed & _BITMASK_BURNED != 0;
ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
}
/**
* @dev Packs ownership data into a single uint256.
*/
function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
owner := and(owner, _BITMASK_ADDRESS)
// `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
}
}
/**
* @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
*/
function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
// For branchless setting of the `nextInitialized` flag.
/// @solidity memory-safe-assembly
assembly {
// `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
}
}
// =============================================================
// APPROVAL OPERATIONS
// =============================================================
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account. See {ERC721A-_approve}.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
*/
function approve(address to, uint256 tokenId) public payable virtual override {
_approve(to, tokenId, true);
}
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
if (!_exists(tokenId)) _revert(ApprovalQueryForNonexistentToken.selector);
return _tokenApprovals[tokenId].value;
}
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom}
* for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
_operatorApprovals[_msgSenderERC721A()][operator] = approved;
emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
}
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted. See {_mint}.
*/
function _exists(uint256 tokenId) internal view virtual returns (bool result) {
if (_startTokenId() <= tokenId) {
if (tokenId < _currentIndex) {
uint256 packed;
while ((packed = _packedOwnerships[tokenId]) == 0) --tokenId;
result = packed & _BITMASK_BURNED == 0;
}
}
}
/**
* @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
*/
function _isSenderApprovedOrOwner(
address approvedAddress,
address owner,
address msgSender
) private pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
owner := and(owner, _BITMASK_ADDRESS)
// Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
msgSender := and(msgSender, _BITMASK_ADDRESS)
// `msgSender == owner || msgSender == approvedAddress`.
result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
}
}
/**
* @dev Returns the storage slot and value for the approved address of `tokenId`.
*/
function _getApprovedSlotAndAddress(uint256 tokenId)
private
view
returns (uint256 approvedAddressSlot, address approvedAddress)
{
TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
// The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
/// @solidity memory-safe-assembly
assembly {
approvedAddressSlot := tokenApproval.slot
approvedAddress := sload(approvedAddressSlot)
}
}
// =============================================================
// TRANSFER OPERATIONS
// =============================================================
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
// Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean.
from = address(uint160(uint256(uint160(from)) & _BITMASK_ADDRESS));
if (address(uint160(prevOwnershipPacked)) != from) _revert(TransferFromIncorrectOwner.selector);
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
// The nested ifs save around 20+ gas over a compound boolean condition.
if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
_beforeTokenTransfers(from, to, tokenId, 1);
// Clear approvals from the previous owner.
/// @solidity memory-safe-assembly
assembly {
if approvedAddress {
// This is equivalent to `delete _tokenApprovals[tokenId]`.
sstore(approvedAddressSlot, 0)
}
}
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
// Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
unchecked {
// We can directly increment and decrement the balances.
--_packedAddressData[from]; // Updates: `balance -= 1`.
++_packedAddressData[to]; // Updates: `balance += 1`.
// Updates:
// - `address` to the next owner.
// - `startTimestamp` to the timestamp of transfering.
// - `burned` to `false`.
// - `nextInitialized` to `true`.
_packedOwnerships[tokenId] = _packOwnershipData(
to,
_BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
);
// If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
uint256 nextTokenId = tokenId + 1;
// If the next slot's address is zero and not burned (i.e. packed value is zero).
if (_packedOwnerships[nextTokenId] == 0) {
// If the next slot is within bounds.
if (nextTokenId != _currentIndex) {
// Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
_packedOwnerships[nextTokenId] = prevOwnershipPacked;
}
}
}
}
// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
/// @solidity memory-safe-assembly
assembly {
// Emit the `Transfer` event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
from, // `from`.
toMasked, // `to`.
tokenId // `tokenId`.
)
}
if (toMasked == 0) _revert(TransferToZeroAddress.selector);
_afterTokenTransfers(from, to, tokenId, 1);
}
/**
* @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override {
safeTransferFrom(from, to, tokenId, '');
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) public payable virtual override {
transferFrom(from, to, tokenId);
if (to.code.length != 0)
if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
}
/**
* @dev Hook that is called before a set of serially-ordered token IDs
* are about to be transferred. This includes minting.
* And also called before burning one token.
*
* `startTokenId` - the first token ID to be transferred.
* `quantity` - the amount to be transferred.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, `tokenId` will be burned by `from`.
* - `from` and `to` are never both zero.
*/
function _beforeTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
/**
* @dev Hook that is called after a set of serially-ordered token IDs
* have been transferred. This includes minting.
* And also called after one token has been burned.
*
* `startTokenId` - the first token ID to be transferred.
* `quantity` - the amount to be transferred.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
* transferred to `to`.
* - When `from` is zero, `tokenId` has been minted for `to`.
* - When `to` is zero, `tokenId` has been burned by `from`.
* - `from` and `to` are never both zero.
*/
function _afterTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
*
* `from` - Previous owner of the given token ID.
* `to` - Target address that will receive the token.
* `tokenId` - Token ID to be transferred.
* `_data` - Optional data to send along with the call.
*
* Returns whether the call correctly returned the expected magic value.
*/
function _checkContractOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory _data
) private returns (bool) {
try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
bytes4 retval
) {
return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
// =============================================================
// MINT OPERATIONS
// =============================================================
/**
* @dev Mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `quantity` must be greater than 0.
*
* Emits a {Transfer} event for each mint.
*/
function _mint(address minter, address to, uint256 quantity) internal virtual {
uint256 startTokenId = _currentIndex;
if (quantity == 0) _revert(MintZeroQuantity.selector);
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are incredibly unrealistic.
// `balance` and `numberMinted` have a maximum limit of 2**64.
// `tokenId` has a maximum limit of 2**256.
unchecked {
// Updates:
// - `address` to the owner.
// - `startTimestamp` to the timestamp of minting.
// - `burned` to `false`.
// - `nextInitialized` to `quantity == 1`.
_packedOwnerships[startTokenId] = _packOwnershipData(
to,
_nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
);
// Updates:
// - `balance += quantity`.
// - `numberMinted += quantity`.
//
// We can directly add to the `balance` and `numberMinted`.
if(minter == to) {
_packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
} else {
_packedAddressData[minter] += quantity * (1 << _BITPOS_NUMBER_MINTED);
_packedAddressData[to] += quantity;
}
// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
if (toMasked == 0) _revert(MintToZeroAddress.selector);
uint256 end = startTokenId + quantity;
uint256 tokenId = startTokenId;
do {
/// @solidity memory-safe-assembly
assembly {
// Emit the `Transfer` event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
0, // `address(0)`.
toMasked, // `to`.
tokenId // `tokenId`.
)
}
// The `!=` check ensures that large values of `quantity`
// that overflows uint256 will make the loop run out of gas.
} while (++tokenId != end);
_currentIndex = end;
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Mints `quantity` tokens and transfers them to `to`.
*
* This function is intended for efficient minting only during contract creation.
*
* It emits only one {ConsecutiveTransfer} as defined in
* [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
* instead of a sequence of {Transfer} event(s).
*
* Calling this function outside of contract creation WILL make your contract
* non-compliant with the ERC721 standard.
* For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
* {ConsecutiveTransfer} event is only permissible during contract creation.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `quantity` must be greater than 0.
*
* Emits a {ConsecutiveTransfer} event.
*/
function _mintERC2309(address to, uint256 quantity) internal virtual {
uint256 startTokenId = _currentIndex;
if (to == address(0)) _revert(MintToZeroAddress.selector);
if (quantity == 0) _revert(MintZeroQuantity.selector);
if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) _revert(MintERC2309QuantityExceedsLimit.selector);
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are unrealistic due to the above check for `quantity` to be below the limit.
unchecked {
// Updates:
// - `balance += quantity`.
// - `numberMinted += quantity`.
//
// We can directly add to the `balance` and `numberMinted`.
_packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
// Updates:
// - `address` to the owner.
// - `startTimestamp` to the timestamp of minting.
// - `burned` to `false`.
// - `nextInitialized` to `quantity == 1`.
_packedOwnerships[startTokenId] = _packOwnershipData(
to,
_nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
);
emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
_currentIndex = startTokenId + quantity;
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Safely mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
* - `quantity` must be greater than 0.
*
* See {_mint}.
*
* Emits a {Transfer} event for each mint.
*/
function _safeMint(
address minter,
address to,
uint256 quantity,
bytes memory _data
) internal virtual {
_mint(minter, to, quantity);
unchecked {
if (to.code.length != 0) {
uint256 end = _currentIndex;
uint256 index = end - quantity;
do {
if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
} while (index < end);
// Reentrancy protection.
if (_currentIndex != end) _revert(bytes4(0));
}
}
}
/**
* @dev Equivalent to `_safeMint(to, quantity, '')`.
*/
function _safeMint(address minter, address to, uint256 quantity) internal virtual {
_safeMint(minter, to, quantity, '');
}
// =============================================================
// APPROVAL OPERATIONS
// =============================================================
/**
* @dev Equivalent to `_approve(to, tokenId, false)`.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_approve(to, tokenId, false);
}
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the
* zero address clears previous approvals.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function _approve(
address to,
uint256 tokenId,
bool approvalCheck
) internal virtual {
address owner = ownerOf(tokenId);
if (approvalCheck && _msgSenderERC721A() != owner)
if (!isApprovedForAll(owner, _msgSenderERC721A())) {
_revert(ApprovalCallerNotOwnerNorApproved.selector);
}
_tokenApprovals[tokenId].value = to;
emit Approval(owner, to, tokenId);
}
// =============================================================
// BURN OPERATIONS
// =============================================================
/**
* @dev Equivalent to `_burn(tokenId, false)`.
*/
function _burn(uint256 tokenId) internal virtual {
_burn(tokenId, false);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
address from = address(uint160(prevOwnershipPacked));
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
if (approvalCheck) {
// The nested ifs save around 20+ gas over a compound boolean condition.
if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
}
_beforeTokenTransfers(from, address(0), tokenId, 1);
// Clear approvals from the previous owner.
/// @solidity memory-safe-assembly
assembly {
if approvedAddress {
// This is equivalent to `delete _tokenApprovals[tokenId]`.
sstore(approvedAddressSlot, 0)
}
}
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
// Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
unchecked {
// Updates:
// - `balance -= 1`.
// - `numberBurned += 1`.
//
// We can directly decrement the balance, and increment the number burned.
// This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
_packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;
// Updates:
// - `address` to the last owner.
// - `startTimestamp` to the timestamp of burning.
// - `burned` to `true`.
// - `nextInitialized` to `true`.
_packedOwnerships[tokenId] = _packOwnershipData(
from,
(_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
);
// If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
uint256 nextTokenId = tokenId + 1;
// If the next slot's address is zero and not burned (i.e. packed value is zero).
if (_packedOwnerships[nextTokenId] == 0) {
// If the next slot is within bounds.
if (nextTokenId != _currentIndex) {
// Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
_packedOwnerships[nextTokenId] = prevOwnershipPacked;
}
}
}
}
emit Transfer(from, address(0), tokenId);
_afterTokenTransfers(from, address(0), tokenId, 1);
// Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
unchecked {
_burnCounter++;
}
}
// =============================================================
// EXTRA DATA OPERATIONS
// =============================================================
/**
* @dev Directly sets the extra data for the ownership data `index`.
*/
function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
uint256 packed = _packedOwnerships[index];
if (packed == 0) _revert(OwnershipNotInitializedForExtraData.selector);
uint256 extraDataCasted;
// Cast `extraData` with assembly to avoid redundant masking.
/// @solidity memory-safe-assembly
assembly {
extraDataCasted := extraData
}
packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
_packedOwnerships[index] = packed;
}
/**
* @dev Called during each token transfer to set the 24bit `extraData` field.
* Intended to be overridden by the cosumer contract.
*
* `previousExtraData` - the value of `extraData` before transfer.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, `tokenId` will be burned by `from`.
* - `from` and `to` are never both zero.
*/
function _extraData(
address from,
address to,
uint24 previousExtraData
) internal view virtual returns (uint24) {}
/**
* @dev Returns the next extra data for the packed ownership data.
* The returned result is shifted into position.
*/
function _nextExtraData(
address from,
address to,
uint256 prevOwnershipPacked
) private view returns (uint256) {
uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
}
// =============================================================
// OTHER OPERATIONS
// =============================================================
/**
* @dev Returns the message sender (defaults to `msg.sender`).
*
* If you are writing GSN compatible contracts, you need to override this function.
*/
function _msgSenderERC721A() internal view virtual returns (address) {
return msg.sender;
}
/**
* @dev Converts a uint256 to its ASCII string decimal representation.
*/
function _toString(uint256 value) internal pure virtual returns (string memory str) {
/// @solidity memory-safe-assembly
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 {} {
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)
}
}
/**
* @dev For more efficient reverts.
*/
function _revert(bytes4 errorSelector) internal pure {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, errorSelector)
revert(0x00, 0x04)
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.0;
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*
* _Available since v3.1._
*/
interface IERC1155 {
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 value
);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(
address indexed account,
address indexed operator,
bool approved
);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(
address account,
uint256 id
) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(
address account,
address operator
) external view returns (bool);
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IERC4906 {
/// @dev This event emits when the metadata of a token is changed.
/// So that the third-party platforms such as NFT market could
/// timely update the images and related attributes of the NFT.
event MetadataUpdate(uint256 _tokenId);
/// @dev This event emits when the metadata of a range of tokens is changed.
/// So that the third-party platforms such as NFT market could
/// timely update the images and related attributes of the NFTs.
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs
pragma solidity ^0.8.4;
/**
* @dev Interface of ERC721A.
*/
interface IERC721A {
/**
* The caller must own the token or be an approved operator.
*/
error ApprovalCallerNotOwnerNorApproved();
/**
* The token does not exist.
*/
error ApprovalQueryForNonexistentToken();
/**
* Cannot query the balance for the zero address.
*/
error BalanceQueryForZeroAddress();
/**
* Cannot mint to the zero address.
*/
error MintToZeroAddress();
/**
* The quantity of tokens minted must be more than zero.
*/
error MintZeroQuantity();
/**
* The token does not exist.
*/
error OwnerQueryForNonexistentToken();
/**
* The caller must own the token or be an approved operator.
*/
error TransferCallerNotOwnerNorApproved();
/**
* The token must be owned by `from`.
*/
error TransferFromIncorrectOwner();
/**
* Cannot safely transfer to a contract that does not implement the
* ERC721Receiver interface.
*/
error TransferToNonERC721ReceiverImplementer();
/**
* Cannot transfer to the zero address.
*/
error TransferToZeroAddress();
/**
* The token does not exist.
*/
error URIQueryForNonexistentToken();
/**
* The `quantity` minted with ERC2309 exceeds the safety limit.
*/
error MintERC2309QuantityExceedsLimit();
/**
* The `extraData` cannot be set on an unintialized ownership slot.
*/
error OwnershipNotInitializedForExtraData();
/**
* Cannot mint a token that already exists.
*/
error TokenAlreadyExists();
// =============================================================
// STRUCTS
// =============================================================
struct TokenOwnership {
// The address of the owner.
address addr;
// Stores the start time of ownership with minimal overhead for tokenomics.
uint64 startTimestamp;
// Whether the token has been burned.
bool burned;
// Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
uint24 extraData;
}
// =============================================================
// TOKEN COUNTERS
// =============================================================
/**
* @dev Returns the total number of tokens in existence.
* Burned tokens will reduce the count.
* To get the total number of tokens minted, please see {_totalMinted}.
*/
function totalSupply() external view returns (uint256);
// =============================================================
// IERC165
// =============================================================
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
// =============================================================
// IERC721
// =============================================================
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables
* (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in `owner`'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`,
* checking first that contract recipients are aware of the ERC721 protocol
* to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move
* this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external payable;
/**
* @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom}
* whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the
* zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external payable;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom}
* for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
// =============================================================
// IERC721Metadata
// =============================================================
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
// =============================================================
// IERC2309
// =============================================================
/**
* @dev Emitted when tokens in `fromTokenId` to `toTokenId`
* (inclusive) is transferred from `from` to `to`, as defined in the
* [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
*
* See {_mintERC2309} for more details.
*/
event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title ILayerr1155
* @author 0xth0mas (Layerr)
* @notice ILayerr1155 interface defines functions required in an ERC1155 token contract to callable by the LayerrMinter contract.
*/
interface ILayerr1155 {
/**
* @notice Mints tokens to the recipients, each recipient gets the corresponding tokenId in the `tokenIds` array
* @dev This function should be protected by a role so that it is not callable by any address
* @dev `recipients`, `tokenIds` and `amounts` arrays must be equal length, each recipient will receive the corresponding
* tokenId and amount from the `tokenIds` and `amounts` arrays
* @param recipients addresses to airdrop tokens to
* @param tokenIds ids of tokens to be airdropped to recipients
* @param amounts amounts of tokens to be airdropped to recipients
*/
function airdrop(address[] calldata recipients, uint256[] calldata tokenIds, uint256[] calldata amounts) external;
/**
* @notice Mints `amount` of `tokenId` to `to`.
* @dev `minter` and `to` may be the same address but are passed as two separate parameters to properly account for
* allowlist mints where a minter is using a delegated wallet to mint
* @param minter address that the mint count will be credited to
* @param to address that will receive the tokens
* @param tokenId id of the token to mint
* @param amount amount of token to mint
*/
function mintTokenId(address minter, address to, uint256 tokenId, uint256 amount) external;
/**
* @notice Mints `amount` of `tokenId` to `to`.
* @dev `minter` and `to` may be the same address but are passed as two separate parameters to properly account for
* allowlist mints where a minter is using a delegated wallet to mint
* @param minter address that the mint count will be credited to
* @param to address that will receive the tokens
* @param tokenIds array of ids to mint
* @param amounts array of amounts to mint
*/
function mintBatchTokenIds(
address minter,
address to,
uint256[] calldata tokenIds,
uint256[] calldata amounts
) external;
/**
* @notice Burns `tokenId` from `from` address
* @dev This function should check that the caller has permission to burn tokens on behalf of `from`
* @param from address to burn the tokenId from
* @param tokenId id of token to be burned
* @param amount amount of `tokenId` to burn from `from`
*/
function burnTokenId(
address from,
uint256 tokenId,
uint256 amount
) external;
/**
* @notice Burns `tokenId` from `from` address
* @dev This function should check that the caller has permission to burn tokens on behalf of `from`
* @param from address to burn the tokenId from
* @param tokenIds array of token ids to be burned
* @param amounts array of amounts to burn from `from`
*/
function burnBatchTokenIds(
address from,
uint256[] calldata tokenIds,
uint256[] calldata amounts
) external;
/**
* @notice Emits URI event for tokens provided
* @param tokenIds array of token ids to emit MetadataUpdate event for
*/
function updateMetadataSpecificTokens(uint256[] calldata tokenIds) external;
/**
* @notice Returns the total supply of ERC1155 tokens in circulation for given `id`.
* @param id the token id to check total supply of
*/
function totalSupply(uint256 id) external view returns (uint256);
/**
* @notice Returns the total number of tokens minted for the contract and the number of tokens minted by the `minter`
* @param minter address to check for number of tokens minted
* @param id the token id to check number of tokens minted for
* @return totalMinted total number of ERC1155 tokens for given `id` minted since token launch
* @return minterMinted total number of ERC1155 tokens for given `id` minted by the `minter`
*/
function totalMintedCollectionAndMinter(address minter, uint256 id) external view returns(uint256 totalMinted, uint256 minterMinted);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title ILayerr721A
* @author 0xth0mas (Layerr)
* @notice ILayerr721A interface defines functions required in an ERC721A token contract to callable by the LayerrMinter contract.
* @dev ILayerr721A should be used for sequential token minting.
*/
interface ILayerr721A {
/// @dev Thrown when two or more sets of arrays are supplied that require equal lengths but differ in length.
error ArrayLengthMismatch();
/**
* @notice Mints tokens to the recipients, each recipient receives the corresponding amount of tokens in the `amounts` array
* @dev This function should be protected by a role so that it is not callable by any address
* @param recipients addresses to airdrop tokens to
* @param amounts amount of tokens that should be airdropped to each recipient
*/
function airdrop(address[] calldata recipients, uint256[] calldata amounts) external;
/**
* @notice Sequentially mints `quantity` of tokens to `to`
* @dev `minter` and `to` may be the same address but are passed as two separate parameters to properly account for
* allowlist mints where a minter is using a delegated wallet to mint
* @param minter address that the mint count will be credited to
* @param to address that will receive the tokens
* @param quantity the number of tokens to sequentially mint to `to`
*/
function mintSequential(address minter, address to, uint256 quantity) external;
/**
* @notice Burns `tokenId` from `from` address
* @dev This function should check that the caller has permission to burn tokens on behalf of `from`
* @param from address to burn the tokenId from
* @param tokenId id of token to be burned
*/
function burnTokenId(address from, uint256 tokenId) external;
/**
* @notice Burns `tokenIds` from `from` address
* @dev This function should check that the caller has permission to burn tokens on behalf of `from`
* @param from address to burn the tokenIds from
* @param tokenIds ids of tokens to be burned
*/
function burnBatchTokenIds(
address from,
uint256[] calldata tokenIds
) external;
/**
* @notice Emits ERC-4906 BatchMetadataUpdate event for all tokens
*/
function updateMetadataAllTokens() external;
/**
* @notice Emits ERC-4906 MetadataUpdate event for tokens provided
* @param tokenIds array of token ids to emit MetadataUpdate event for
*/
function updateMetadataSpecificTokens(uint256[] calldata tokenIds) external;
/**
* @notice Returns the total supply of ERC721 tokens in circulation.
*/
function totalSupply() external view returns (uint256);
/**
* @notice Returns the total number of tokens minted for the contract and the number of tokens minted by the `minter`
* @param minter address to check for number of tokens minted
* @return totalMinted total number of ERC721 tokens minted since token launch
* @return minterMinted total number of ERC721 tokens minted by the `minter`
*/
function totalMintedCollectionAndMinter(address minter) external view returns(uint256 totalMinted, uint256 minterMinted);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {MintOrder, MintParameters, MintToken, BurnToken, PaymentToken} from "../lib/MinterStructs.sol";
/**
* @title ILayerrMinter
* @author 0xth0mas (Layerr)
* @notice ILayerrMinter interface defines functions required in the LayerrMinter to be callable by token contracts
*/
interface ILayerrMinter {
/// @dev Event emitted when a mint order is fulfilled
event MintOrderFulfilled(
bytes32 indexed mintParametersDigest,
address indexed minter,
uint256 indexed quantity
);
/// @dev Event emitted when a token contract updates an allowed signer for EIP712 signatures
event ContractAllowedSignerUpdate(
address indexed _contract,
address indexed _signer,
bool indexed _allowed
);
/// @dev Event emitted when a token contract updates an allowed oracle signer for offchain authorization of a wallet to use a signature
event ContractOracleUpdated(
address indexed _contract,
address indexed _oracle,
bool indexed _allowed
);
/// @dev Event emitted when a signer updates their nonce with LayerrMinter. Updating a nonce invalidates all previously signed EIP712 signatures.
event SignerNonceIncremented(
address indexed _signer,
uint256 indexed _nonce
);
/// @dev Event emitted when a specific signature's validity is updated with the LayerrMinter contract.
event SignatureValidityUpdated(
address indexed _contract,
bool indexed invalid,
bytes32 mintParametersDigests
);
/// @dev Thrown when the amount of native tokens supplied in msg.value is insufficient for the mint order
error InsufficientPayment();
/// @dev Thrown when a payment fails to be forwarded to the intended recipient
error PaymentFailed();
/// @dev Thrown when a MintParameters payment token uses a token type value other than native or ERC20
error InvalidPaymentTokenType();
/// @dev Thrown when a MintParameters burn token uses a token type value other than ERC20, ERC721 or ERC1155
error InvalidBurnTokenType();
/// @dev Thrown when a MintParameters mint token uses a token type value other than ERC20, ERC721 or ERC1155
error InvalidMintTokenType();
/// @dev Thrown when a MintParameters burn token uses a burn type value other than contract burn or send to dead
error InvalidBurnType();
/// @dev Thrown when a MintParameters burn token requires a specific burn token id and the tokenId supplied does not match
error InvalidBurnTokenId();
/// @dev Thrown when a MintParameters burn token requires a specific ERC721 token and the burn amount is greater than 1
error CannotBurnMultipleERC721WithSameId();
/// @dev Thrown when attempting to mint with MintParameters that have a start time greater than the current block time
error MintHasNotStarted();
/// @dev Thrown when attempting to mint with MintParameters that have an end time less than the current block time
error MintHasEnded();
/// @dev Thrown when a MintParameters has a merkleroot set but the supplied merkle proof is invalid
error InvalidMerkleProof();
/// @dev Thrown when a MintOrder will cause a token's minted supply to exceed the defined maximum supply in MintParameters
error MintExceedsMaxSupply();
/// @dev Thrown when a MintOrder will cause a minter's minted amount to exceed the defined max per wallet in MintParameters
error MintExceedsMaxPerWallet();
/// @dev Thrown when a MintParameters mint token has a specific ERC721 token and the mint amount is greater than 1
error CannotMintMultipleERC721WithSameId();
/// @dev Thrown when the recovered signer for the MintParameters is not an allowed signer for the mint token
error NotAllowedSigner();
/// @dev Thrown when the recovered signer's nonce does not match the current nonce in LayerrMinter
error SignerNonceInvalid();
/// @dev Thrown when a signature has been marked as invalid for a mint token contract
error SignatureInvalid();
/// @dev Thrown when MintParameters requires an oracle signature and the recovered signer is not an allowed oracle for the contract
error InvalidOracleSignature();
/// @dev Thrown when MintParameters has a max signature use set and the MintOrder will exceed the maximum uses
error ExceedsMaxSignatureUsage();
/// @dev Thrown when attempting to increment nonce on behalf of another account and the signature is invalid
error InvalidSignatureToIncrementNonce();
/**
* @notice This function is called by token contracts to update allowed signers for minting
* @param _signer address of the EIP712 signer
* @param _allowed if the `_signer` is allowed to sign for minting
*/
function setContractAllowedSigner(address _signer, bool _allowed) external;
/**
* @notice This function is called by token contracts to update allowed oracles for offchain authorizations
* @param _oracle address of the oracle
* @param _allowed if the `_oracle` is allowed to sign offchain authorizations
*/
function setContractAllowedOracle(address _oracle, bool _allowed) external;
/**
* @notice This function is called by token contracts to update validity of signatures for the LayerrMinter contract
* @dev `invalid` should be true to invalidate signatures, the default state of `invalid` being false means
* a signature is valid for a contract assuming all other conditions are met
* @param mintParametersDigests an array of message digests for MintParameters to update validity of
* @param invalid if the supplied digests will be marked as valid or invalid
*/
function setSignatureValidity(
bytes32[] calldata mintParametersDigests,
bool invalid
) external;
/**
* @notice Increments the nonce for a signer to invalidate all previous signed MintParameters
*/
function incrementSignerNonce() external;
/**
* @notice Increments the nonce on behalf of another account by validating a signature from that account
* @dev The signature is an eth personal sign message of the current signer nonce plus the chain id
* ex. current nonce 0 on chain 5 would be a signature of \x19Ethereum Signed Message:\n15
* current nonce 50 on chain 1 would be a signature of \x19Ethereum Signed Message:\n251
* @param signer account to increment nonce for
* @param signature signature proof that the request is coming from the account
*/
function incrementNonceFor(address signer, bytes calldata signature) external;
/**
* @notice Validates and processes a single MintOrder, tokens are minted to msg.sender
* @param mintOrder struct containing the details of the mint order
*/
function mint(
MintOrder calldata mintOrder
) external payable;
/**
* @notice Validates and processes an array of MintOrders, tokens are minted to msg.sender
* @param mintOrders array of structs containing the details of the mint orders
*/
function mintBatch(
MintOrder[] calldata mintOrders
) external payable;
/**
* @notice Validates and processes a single MintOrder, tokens are minted to `mintToWallet`
* @param mintToWallet the address tokens will be minted to
* @param mintOrder struct containing the details of the mint order
* @param paymentContext Contextual information related to the payment process
* (Note: This parameter is required for integration with
* the payment processor and does not impact the behavior
* of the function)
*/
function mintTo(
address mintToWallet,
MintOrder calldata mintOrder,
uint256 paymentContext
) external payable;
/**
* @notice Validates and processes an array of MintOrders, tokens are minted to `mintToWallet`
* @param mintToWallet the address tokens will be minted to
* @param mintOrders array of structs containing the details of the mint orders
* @param paymentContext Contextual information related to the payment process
* (Note: This parameter is required for integration with
* the payment processor and does not impact the behavior
* of the function)
*/
function mintBatchTo(
address mintToWallet,
MintOrder[] calldata mintOrders,
uint256 paymentContext
) external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ERC165} from "./IERC165.sol";
/**
* @title ILayerrRenderer
* @author 0xth0mas (Layerr)
* @notice ILayerrRenderer interface defines functions required in LayerrRenderer to be callable by token contracts
*/
interface ILayerrRenderer is ERC165 {
enum RenderType {
LAYERR_HOSTED,
PREREVEAL,
BASE_PLUS_TOKEN
}
/// @dev Thrown when a payment fails for Layerr-hosted IPFS
error PaymentFailed();
/// @dev Thrown when a call is made for an owner-function by a non-contract owner
error NotContractOwner();
/// @dev Thrown when a signature is not made by the authorized account
error InvalidSignature();
/**
* @notice Generates a tokenURI for the `contractAddress` and `tokenId`
* @param contractAddress token contract address to render a token URI for
* @param tokenId token id to render
* @return uri for the token metadata
*/
function tokenURI(
address contractAddress,
uint256 tokenId
) external view returns (string memory);
/**
* @notice Generates a contractURI for the `contractAddress`
* @param contractAddress contract address to render a contract URI for
* @return uri for the contract metadata
*/
function contractURI(
address contractAddress
) external view returns (string memory);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title ILayerrToken
* @author 0xth0mas (Layerr)
* @notice ILayerrToken interface defines functions required to be supported by the Layerr platform
*/
interface ILayerrToken {
/// @dev Emitted when the contract is deployed so that it can be indexed and assigned to its owner
event LayerrContractDeployed();
/// @dev Emitted when a mint extension is updated to allowed or disallowed
event MintExtensionUpdated(address mintExtension, bool allowed);
/// @dev Emitted when the contract's renderer is updated
event RendererUpdated(address renderer);
/// @dev Thrown when a caller that is not a mint extension attempts to execute a mint function
error NotValidMintingExtension();
/// @dev Thrown when a non-owner attempts to execute an only owner function
error NotAuthorized();
/// @dev Thrown when attempting to withdraw funds from the contract and the call fails
error WithdrawFailed();
/**
* @return name the name of the token
*/
function name() external view returns(string memory);
/**
* @return symbol the token symbol
*/
function symbol() external view returns(string memory);
/**
* @return renderer the address that will render token/contract URIs
*/
function renderer() external view returns(address);
/**
* @notice Sets the renderer for token/contract URIs
* @dev This function should be restricted to contract owners
* @param _renderer address to set as the token/contract URI renderer
*/
function setRenderer(address _renderer) external;
/**
* @notice Sets whether or not an address is allowed to call minting functions
* @dev This function should be restricted to contract owners
* @param _extension address of the mint extension to update
* @param _allowed if the mint extension is allowed to mint tokens
*/
function setMintExtension(
address _extension,
bool _allowed
) external;
/**
* @notice This function calls the mint extension to update `_signer`'s allowance
* @dev This function should be restricted to contract owners
* @param _extension address of the mint extension to update
* @param _signer address of the signer to update
* @param _allowed if `_signer` is allowed to sign for `_extension`
*/
function setContractAllowedSigner(
address _extension,
address _signer,
bool _allowed
) external;
/**
* @notice This function calls the mint extension to update `_oracle`'s allowance
* @dev This function should be restricted to contract owners
* @param _extension address of the mint extension to update
* @param _oracle address of the oracle to update
* @param _allowed if `_oracle` is allowed to sign for `_extension`
*/
function setContractAllowedOracle(
address _extension,
address _oracle,
bool _allowed
) external;
/**
* @notice This function calls the mint extension to update signature validity
* @dev This function should be restricted to contract owners
* @param _extension address of the mint extension to update
* @param signatureDigests hash digests of signatures parameters to update
* @param invalid true if the signature digests should be marked as invalid
*/
function setSignatureValidity(
address _extension,
bytes32[] calldata signatureDigests,
bool invalid
) external;
/**
* @notice This function updates the ERC2981 royalty percentages
* @dev This function should be restricted to contract owners
* @param pct royalty percentage in BPS
* @param royaltyReciever address to receive royalties
*/
function setRoyalty(
uint96 pct,
address royaltyReciever
) external;
/**
* @notice This function updates the token contract's name and symbol
* @dev This function should be restricted to contract owners
* @param _name new name for the token contract
* @param _symbol new symbol for the token contract
*/
function editContract(
string calldata _name,
string calldata _symbol
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
interface ILayerrsOfSummerRenderer {
function lockMetadata(uint256 tokenId) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
interface IOperatorFilterRegistry {
/**
* @notice Returns true if operator is not filtered for a given token, either by address or codeHash. Also returns
* true if supplied registrant address is not registered.
*/
function isOperatorAllowed(address registrant, address operator) external view returns (bool);
/**
* @notice Registers an address with the registry. May be called by address itself or by EIP-173 owner.
*/
function register(address registrant) external;
/**
* @notice Registers an address with the registry and "subscribes" to another address's filtered operators and codeHashes.
*/
function registerAndSubscribe(address registrant, address subscription) external;
/**
* @notice Registers an address with the registry and copies the filtered operators and codeHashes from another
* address without subscribing.
*/
function registerAndCopyEntries(address registrant, address registrantToCopy) external;
/**
* @notice Unregisters an address with the registry and removes its subscription. May be called by address itself or by EIP-173 owner.
* Note that this does not remove any filtered addresses or codeHashes.
* Also note that any subscriptions to this registrant will still be active and follow the existing filtered addresses and codehashes.
*/
function unregister(address addr) external;
/**
* @notice Update an operator address for a registered address - when filtered is true, the operator is filtered.
*/
function updateOperator(address registrant, address operator, bool filtered) external;
/**
* @notice Update multiple operators for a registered address - when filtered is true, the operators will be filtered. Reverts on duplicates.
*/
function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
/**
* @notice Update a codeHash for a registered address - when filtered is true, the codeHash is filtered.
*/
function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
/**
* @notice Update multiple codeHashes for a registered address - when filtered is true, the codeHashes will be filtered. Reverts on duplicates.
*/
function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
/**
* @notice Subscribe an address to another registrant's filtered operators and codeHashes. Will remove previous
* subscription if present.
* Note that accounts with subscriptions may go on to subscribe to other accounts - in this case,
* subscriptions will not be forwarded. Instead the former subscription's existing entries will still be
* used.
*/
function subscribe(address registrant, address registrantToSubscribe) external;
/**
* @notice Unsubscribe an address from its current subscribed registrant, and optionally copy its filtered operators and codeHashes.
*/
function unsubscribe(address registrant, bool copyExistingEntries) external;
/**
* @notice Get the subscription address of a given registrant, if any.
*/
function subscriptionOf(address addr) external returns (address registrant);
/**
* @notice Get the set of addresses subscribed to a given registrant.
* Note that order is not guaranteed as updates are made.
*/
function subscribers(address registrant) external returns (address[] memory);
/**
* @notice Get the subscriber at a given index in the set of addresses subscribed to a given registrant.
* Note that order is not guaranteed as updates are made.
*/
function subscriberAt(address registrant, uint256 index) external returns (address);
/**
* @notice Copy filtered operators and codeHashes from a different registrantToCopy to addr.
*/
function copyEntriesOf(address registrant, address registrantToCopy) external;
/**
* @notice Returns true if operator is filtered by a given address or its subscription.
*/
function isOperatorFiltered(address registrant, address operator) external returns (bool);
/**
* @notice Returns true if the hash of an address's code is filtered by a given address or its subscription.
*/
function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
/**
* @notice Returns true if a codeHash is filtered by a given address or its subscription.
*/
function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
/**
* @notice Returns a list of filtered operators for a given address or its subscription.
*/
function filteredOperators(address addr) external returns (address[] memory);
/**
* @notice Returns the set of filtered codeHashes for a given address or its subscription.
* Note that order is not guaranteed as updates are made.
*/
function filteredCodeHashes(address addr) external returns (bytes32[] memory);
/**
* @notice Returns the filtered operator at the given index of the set of filtered operators for a given address or
* its subscription.
* Note that order is not guaranteed as updates are made.
*/
function filteredOperatorAt(address registrant, uint256 index) external returns (address);
/**
* @notice Returns the filtered codeHash at the given index of the list of filtered codeHashes for a given address or
* its subscription.
* Note that order is not guaranteed as updates are made.
*/
function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
/**
* @notice Returns true if an address has registered
*/
function isRegistered(address addr) external returns (bool);
/**
* @dev Convenience method to compute the code hash of an arbitrary contract
*/
function codeHashOf(address addr) external returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ERC165} from './IERC165.sol';
interface IOwnable is ERC165 {
/// @dev Thrown when a non-owner is attempting to perform an owner function
error NotContractOwner();
/// @dev Emitted when contract ownership is transferred to a new owner
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice Get the address of the owner
/// @return The address of the owner.
function owner() view external returns(address);
/// @notice Set the address of the new owner of the contract
/// @dev Set _newOwner to address(0) to renounce any ownership.
/// @param _newOwner The address of the new owner of the contract
function transferOwnership(address _newOwner) external;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.0 <0.9.0;
import "./ERC721A.sol";
import {IERC4906} from "../../interfaces/IERC4906.sol";
import {ILayerr721A} from "../../interfaces/ILayerr721A.sol";
import {ILayerrRenderer} from "../../interfaces/ILayerrRenderer.sol";
import {LayerrToken} from "../LayerrToken.sol";
import "operator-filter-registry/src/DefaultOperatorFilterer.sol";
/**
* @title Layerr721A
* @author 0xth0mas (Layerr)
* @notice Layerr721A is an ERC721 contract built for the Layerr platform using
* the Chiru Labs ERC721A implementation for gas efficient sequential
* minting.
*/
contract Layerr721A is DefaultOperatorFilterer, ERC721A, ILayerr721A, LayerrToken, IERC4906 {
/** METADATA FUNCTIONS */
/**
* @notice Returns the URI for a given `tokenId`
* @param tokenId id of token to return URI of
* @return tokenURI location of the token metadata
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
return ILayerrRenderer(_getRenderer()).tokenURI(address(this), tokenId);
}
/**
* @notice Returns the URI for the contract metadata
* @return contractURI location of the contract metadata
*/
function contractURI() public view returns (string memory) {
return ILayerrRenderer(_getRenderer()).contractURI(address(this));
}
/**
* @inheritdoc LayerrToken
*/
function name() public view virtual override(LayerrToken, ERC721A) returns (string memory) {
return LayerrToken.name();
}
/**
* @inheritdoc LayerrToken
*/
function symbol() public view virtual override(LayerrToken, ERC721A) returns (string memory) {
return LayerrToken.symbol();
}
/** MINT FUNCTIONS */
/**
* @inheritdoc ILayerr721A
*/
function mintSequential(address minter, address to, uint256 quantity) external virtual onlyMinter {
_mint(minter, to, quantity);
}
/**
* @inheritdoc ILayerr721A
*/
function burnTokenId(address from, uint256 tokenId) external {
if (!isApprovedForAll(from, msg.sender)) {
revert NotAuthorized();
}
if(ownerOf(tokenId) != from) { revert NotAuthorized(); }
_burn(tokenId, true);
}
/**
* @inheritdoc ILayerr721A
*/
function burnBatchTokenIds(
address from,
uint256[] calldata tokenIds
) external {
if (!isApprovedForAll(from, msg.sender)) {
revert NotAuthorized();
}
for(uint256 i = 0;i < tokenIds.length;) {
uint256 tokenId = tokenIds[i];
if(ownerOf(tokenId) != from) { revert NotAuthorized(); }
_burn(tokenId, true);
unchecked { ++i; }
}
}
/**
* @inheritdoc ILayerr721A
*/
function totalSupply() public view override(ERC721A, ILayerr721A) returns (uint256) {
return ERC721A.totalSupply();
}
/**
* @inheritdoc ILayerr721A
*/
function totalMintedCollectionAndMinter(address minter) external view returns(uint256 totalMinted, uint256 minterMinted) {
totalMinted = _totalMinted();
minterMinted = _numberMinted(minter);
}
/** OWNER FUNCTIONS */
/**
* @inheritdoc ILayerr721A
*/
function airdrop(address[] calldata recipients, uint256[] calldata amounts) external onlyOwner {
if(recipients.length != amounts.length) { revert ArrayLengthMismatch(); }
for(uint256 index = 0;index < recipients.length;) {
_mint(msg.sender, recipients[index], amounts[index]);
unchecked { ++index; }
}
}
/**
* @notice Subscribes to an operator filter registry
* @param operatorFilterRegistry operator filter address to subscribe to
*/
function setOperatorFilter(address operatorFilterRegistry) external onlyOwner {
if (operatorFilterRegistry != address(0)) {
OPERATOR_FILTER_REGISTRY.registerAndSubscribe(
address(this),
operatorFilterRegistry
);
}
}
/**
* @notice Unsubscribes from the operator filter registry
*/
function removeOperatorFilter() external onlyOwner {
OPERATOR_FILTER_REGISTRY.unregister(
address(this)
);
}
/**
* @inheritdoc ILayerr721A
*/
function updateMetadataAllTokens() external onlyOwner {
emit BatchMetadataUpdate(0, type(uint256).max);
}
/**
* @inheritdoc ILayerr721A
*/
function updateMetadataSpecificTokens(uint256[] calldata tokenIds) external onlyOwner {
for(uint256 i; i < tokenIds.length; ) {
emit MetadataUpdate(tokenIds[i]);
unchecked { ++i; }
}
}
/** OPERATOR FILTER OVERRIDES */
function setApprovalForAll(
address operator,
bool approved
) public override onlyAllowedOperatorApproval(operator) {
super.setApprovalForAll(operator, approved);
}
function approve(
address operator,
uint256 tokenId
) public payable override onlyAllowedOperatorApproval(operator) {
super.approve(operator, tokenId);
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public payable override virtual onlyAllowedOperator(from) {
super.transferFrom(from, to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public payable override virtual onlyAllowedOperator(from) {
super.safeTransferFrom(from, to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public payable override virtual onlyAllowedOperator(from) {
super.safeTransferFrom(from, to, tokenId, data);
}
/** ERC165 */
function supportsInterface(
bytes4 interfaceId
) public view virtual override(LayerrToken, ERC721A) returns (bool) {
return
interfaceId == type(ILayerr721A).interfaceId ||
LayerrToken.supportsInterface(interfaceId) ||
ERC721A.supportsInterface(interfaceId);
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
import {AddressValue} from "../lib/StorageTypes.sol";
import {IOwnable} from "../interfaces/IOwnable.sol";
import {LAYERROWNABLE_OWNER_SLOT, LAYERROWNABLE_NEW_OWNER_SLOT} from "./LayerrStorage.sol";
/**
* @title LayerrOwnable
* @author 0xth0mas (Layerr)
* @notice ERC173 compliant ownership interface with two-step transfer/acceptance.
* @dev LayerrOwnable uses two custom storage slots for current contract owner and new owner as defined in LayerrStorage.
*/
contract LayerrOwnable is IOwnable {
modifier onlyOwner() {
if (msg.sender != _getOwner()) {
revert NotContractOwner();
}
_;
}
modifier onlyNewOwner() {
if (msg.sender != _getNewOwner()) {
revert NotContractOwner();
}
_;
}
/**
* @notice Returns the current contract owner
*/
function owner() external view returns(address _owner) {
_owner = _getOwner();
}
/**
* @notice Begins first step of ownership transfer. _newOwner will need to call acceptTransfer() to complete.
* @param _newOwner address to transfer ownership of contract to
*/
function transferOwnership(address _newOwner) external onlyOwner {
_setNewOwner(_newOwner);
}
/**
* @notice Second step of ownership transfer called by the new contract owner.
*/
function acceptTransfer() external onlyNewOwner {
address _previousOwner = _getOwner();
//set contract owner to new owner, clear out the newOwner value
_setOwner(_getNewOwner());
_setNewOwner(address(0));
emit OwnershipTransferred(_previousOwner, _getOwner());
}
/**
* @notice Cancels ownership transfer to newOwner before the transfer is accepted.
*/
function cancelTransfer() external onlyOwner {
_setNewOwner(address(0));
}
/**
* @notice EIP165 supportsInterface for introspection
*/
function supportsInterface(bytes4 interfaceID) public view virtual returns (bool) {
return interfaceID == 0x7f5828d0;
}
/** INTERNAL FUNCTIONS */
/**
* @dev Internal helper function to load custom storage slot address value
*/
function _getOwner() internal view returns(address _owner) {
AddressValue storage ownerValue;
/// @solidity memory-safe-assembly
assembly {
ownerValue.slot := LAYERROWNABLE_OWNER_SLOT
}
_owner = ownerValue.value;
}
/**
* @dev Internal helper function to set owner address in custom storage slot
*/
function _setOwner(address _owner) internal {
AddressValue storage ownerValue;
/// @solidity memory-safe-assembly
assembly {
ownerValue.slot := LAYERROWNABLE_OWNER_SLOT
}
ownerValue.value = _owner;
}
/**
* @dev Internal helper function to load custom storage slot address value
*/
function _getNewOwner() internal view returns(address _newOwner) {
AddressValue storage newOwnerValue;
/// @solidity memory-safe-assembly
assembly {
newOwnerValue.slot := LAYERROWNABLE_NEW_OWNER_SLOT
}
_newOwner = newOwnerValue.value;
}
/**
* @dev Internal helper function to set new owner address in custom storage slot
*/
function _setNewOwner(address _newOwner) internal {
AddressValue storage newOwnerValue;
/// @solidity memory-safe-assembly
assembly {
newOwnerValue.slot := LAYERROWNABLE_NEW_OWNER_SLOT
}
newOwnerValue.value = _newOwner;
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
/// @dev Storage slot for current owner calculated from keccak256('Layerr.LayerrOwnable.owner')
bytes32 constant LAYERROWNABLE_OWNER_SLOT = 0xedc628ad38a73ae7d50600532f1bf21da1bfb1390b4f8174f361aca54d4c6b66;
/// @dev Storage slot for pending ownership transfer calculated from keccak256('Layerr.LayerrOwnable.newOwner')
bytes32 constant LAYERROWNABLE_NEW_OWNER_SLOT = 0x15c115ab76de082272ae65126522082d4aad634b6478097549f84086af3b84bc;
/// @dev Storage slot for token name calculated from keccak256('Layerr.LayerrToken.name')
bytes32 constant LAYERRTOKEN_NAME_SLOT = 0x7f84c61ed30727f282b62cab23f49ac7f4d263f04a4948416b7b9ba7f34a20dc;
/// @dev Storage slot for token symbol calculated from keccak256('Layerr.LayerrToken.symbol')
bytes32 constant LAYERRTOKEN_SYMBOL_SLOT = 0xdc0f2363b26c589c72caecd2357dae5fee235863060295a057e8d69d61a96d8a;
/// @dev Storage slot for URI renderer calculated from keccak256('Layerr.LayerrToken.renderer')
bytes32 constant LAYERRTOKEN_RENDERER_SLOT = 0x395b7021d979c3dbed0f5d530785632316942232113ba3dbe325dc167550e320;
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
import {AddressValue, StringValue} from "../lib/StorageTypes.sol";
import {LAYERRTOKEN_NAME_SLOT, LAYERRTOKEN_SYMBOL_SLOT, LAYERRTOKEN_RENDERER_SLOT} from "../common/LayerrStorage.sol";
import {LayerrOwnable} from "../common/LayerrOwnable.sol";
import {ILayerrToken} from "../interfaces/ILayerrToken.sol";
import {ILayerrMinter} from "../interfaces/ILayerrMinter.sol";
import {ERC2981} from "../lib/ERC2981.sol";
/**
* @title LayerrToken
* @author 0xth0mas (Layerr)
* @notice LayerrToken contains the general purpose token contract functions for interacting
* with the Layerr platform.
*/
contract LayerrToken is ILayerrToken, LayerrOwnable, ERC2981 {
/// @dev mapping of allowed mint extensions
mapping(address => bool) public mintingExtensions;
modifier onlyMinter() {
if (!mintingExtensions[msg.sender]) {
revert NotValidMintingExtension();
}
_;
}
/**
* @inheritdoc ILayerrToken
*/
function name() public virtual view returns(string memory _name) {
StringValue storage nameValue;
/// @solidity memory-safe-assembly
assembly {
nameValue.slot := LAYERRTOKEN_NAME_SLOT
}
_name = nameValue.value;
}
/**
* @inheritdoc ILayerrToken
*/
function symbol() public virtual view returns(string memory _symbol) {
StringValue storage symbolValue;
/// @solidity memory-safe-assembly
assembly {
symbolValue.slot := LAYERRTOKEN_SYMBOL_SLOT
}
_symbol = symbolValue.value;
}
/**
* @inheritdoc ILayerrToken
*/
function renderer() external view returns(address _renderer) {
_renderer = _getRenderer();
}
function supportsInterface(bytes4 interfaceId) public view virtual override(LayerrOwnable, ERC2981) returns (bool) {
return interfaceId == type(ILayerrToken).interfaceId ||
ERC2981.supportsInterface(interfaceId) ||
LayerrOwnable.supportsInterface(interfaceId);
}
/* OWNER FUNCTIONS */
/**
* @inheritdoc ILayerrToken
*/
function setRenderer(address _renderer) external onlyOwner {
_setRenderer(_renderer);
emit RendererUpdated(_renderer);
}
/**
* @inheritdoc ILayerrToken
*/
function setMintExtension(
address _extension,
bool _allowed
) external onlyOwner {
mintingExtensions[_extension] = _allowed;
emit MintExtensionUpdated(_extension, _allowed);
}
/**
* @inheritdoc ILayerrToken
*/
function setContractAllowedSigner(
address _extension,
address _signer,
bool _allowed
) external onlyOwner {
ILayerrMinter(_extension).setContractAllowedSigner(_signer, _allowed);
}
/**
* @inheritdoc ILayerrToken
*/
function setContractAllowedOracle(
address _extension,
address _oracle,
bool _allowed
) external onlyOwner {
ILayerrMinter(_extension).setContractAllowedOracle(_oracle, _allowed);
}
/**
* @inheritdoc ILayerrToken
*/
function setSignatureValidity(
address _extension,
bytes32[] calldata signatureDigests,
bool invalid
) external onlyOwner {
ILayerrMinter(_extension).setSignatureValidity(signatureDigests, invalid);
}
/**
* @inheritdoc ILayerrToken
*/
function setRoyalty(
uint96 pct,
address royaltyReciever
) external onlyOwner {
_setDefaultRoyalty(royaltyReciever, pct);
}
/**
* @inheritdoc ILayerrToken
*/
function editContract(
string calldata _name,
string calldata _symbol
) external onlyOwner {
_setName(_name);
_setSymbol(_symbol);
}
/**
* @notice Called during a proxy deployment to emit the LayerrContractDeployed event
*/
function initialize() external onlyOwner {
emit LayerrContractDeployed();
}
/**
* @notice Called to withdraw any funds that may be sent to the contract
*/
function withdraw() external onlyOwner {
(bool sent, ) = payable(_getOwner()).call{value: address(this).balance}("");
if (!sent) {
revert WithdrawFailed();
}
}
/**
* INTERNAL FUNCTIONS
*/
/**
* @notice Internal function to set the renderer address in a custom storage slot location
* @param _renderer address of the renderer to set
*/
function _setRenderer(address _renderer) internal {
AddressValue storage rendererValue;
/// @solidity memory-safe-assembly
assembly {
rendererValue.slot := LAYERRTOKEN_RENDERER_SLOT
}
rendererValue.value = _renderer;
}
/**
* @notice Internal function to get the renderer address from a custom storage slot location
* @return _renderer address of the renderer
*/
function _getRenderer() internal view returns(address _renderer) {
AddressValue storage rendererValue;
/// @solidity memory-safe-assembly
assembly {
rendererValue.slot := LAYERRTOKEN_RENDERER_SLOT
}
_renderer = rendererValue.value;
}
/**
* @notice Internal function to set the token contract name in a custom storage slot location
* @param _name name for the token contract
*/
function _setName(string calldata _name) internal {
StringValue storage nameValue;
/// @solidity memory-safe-assembly
assembly {
nameValue.slot := LAYERRTOKEN_NAME_SLOT
}
nameValue.value = _name;
}
/**
* @notice Internal function to set the token contract symbol in a custom storage slot location
* @param _symbol symbol for the token contract
*/
function _setSymbol(string calldata _symbol) internal {
StringValue storage symbolValue;
/// @solidity memory-safe-assembly
assembly {
symbolValue.slot := LAYERRTOKEN_SYMBOL_SLOT
}
symbolValue.value = _symbol;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import {Layerr721A} from "../lib/Layerr/tokens/ERC721A/Layerr721A.sol";
import {ILayerr1155} from "../lib/Layerr/interfaces/ILayerr1155.sol";
import {IERC1155} from "../lib/Layerr/interfaces/IERC1155.sol";
import {StringValue} from "../lib/Layerr/lib/StorageTypes.sol";
import {AddressValue} from "../lib/Layerr/lib/StorageTypes.sol";
import {ILayerrMinter} from "../lib/Layerr/interfaces/ILayerrMinter.sol";
import {ILayerrsOfSummerRenderer} from "./interfaces/ILayerrsOfSummerRenderer.sol";
import {LAYERROWNABLE_OWNER_SLOT, LAYERRTOKEN_NAME_SLOT, LAYERRTOKEN_SYMBOL_SLOT, LAYERRTOKEN_RENDERER_SLOT} from "../lib/Layerr/common/LayerrStorage.sol";
/**
* @title LayerrsOfSummer
* @author 0xth0mas (Layerr)
* @notice Layerrs of Summer is an onchain generative art collection
* celebrating the launch of Base and Onchain Summer 2023.
*/
contract LayerrsOfSummer is Layerr721A {
ILayerr1155 public colorsOfSummerNFT;
/**
* @notice Initialize the LayerrsOfSummer contract
* @param _name token contract name
* @param _symbol token contract symbol
* @param _extension minting extension to use with the token contract
* @param _renderer renderer to use with the token contract
* @param _signers array of allowed signers for the mint extension
*/
constructor(
string memory _name,
string memory _symbol,
address _extension,
address _renderer,
address[] memory _signers,
address _colorsOfSummerNFT
) {
StringValue storage name;
StringValue storage symbol;
AddressValue storage renderer;
AddressValue storage owner;
/// @solidity memory-safe-assembly
assembly {
name.slot := LAYERRTOKEN_NAME_SLOT
symbol.slot := LAYERRTOKEN_SYMBOL_SLOT
renderer.slot := LAYERRTOKEN_RENDERER_SLOT
owner.slot := LAYERROWNABLE_OWNER_SLOT
}
name.value = _name;
symbol.value = _symbol;
renderer.value = _renderer;
owner.value = tx.origin;
colorsOfSummerNFT = ILayerr1155(_colorsOfSummerNFT);
uint256 signersLength = _signers.length;
for(uint256 signerIndex;signerIndex < signersLength;) {
ILayerrMinter(_extension).setContractAllowedSigner(_signers[signerIndex], true);
unchecked {
++signerIndex;
}
}
}
/** MINT FUNCTIONS */
function mintSequential(address minter, address to, uint256 quantity) external override onlyMinter {
_airdropColorsOfSummer(to);
_mint(minter, to, quantity);
}
/**
* @notice Airdrops a random amount of color tokens to the minter
* if they have not previously minted color tokens.
* @param to address to airdrop colors to
*/
function _airdropColorsOfSummer(address to) internal {
uint256 totalMinted;
uint256 colorMinted;
unchecked {
(, colorMinted) = colorsOfSummerNFT.totalMintedCollectionAndMinter(to, 0);
totalMinted += colorMinted;
(, colorMinted) = colorsOfSummerNFT.totalMintedCollectionAndMinter(to, 1);
totalMinted += colorMinted;
(, colorMinted) = colorsOfSummerNFT.totalMintedCollectionAndMinter(to, 2);
totalMinted += colorMinted;
(, colorMinted) = colorsOfSummerNFT.totalMintedCollectionAndMinter(to, 3);
totalMinted += colorMinted;
(, colorMinted) = colorsOfSummerNFT.totalMintedCollectionAndMinter(to, 4);
totalMinted += colorMinted;
(, colorMinted) = colorsOfSummerNFT.totalMintedCollectionAndMinter(to, 5);
totalMinted += colorMinted;
}
if(totalMinted == 0) {
bool[] memory colorUsed = new bool[](6);
uint256 startSeed = uint256(keccak256(abi.encodePacked(to, block.timestamp)));
uint256 seed = startSeed;
uint256 remainingToMint = 8;
while(true) {
if(seed == 0) {
unchecked { ++startSeed; }
seed = uint256(keccak256(abi.encodePacked(startSeed)));
}
uint256 randomColor = seed % 6;
seed >>= 3;
if(colorUsed[randomColor]) continue;
uint256 mintAmount = seed % remainingToMint;
seed >>= 3;
unchecked { remainingToMint -= mintAmount; }
if(mintAmount > 0) {
colorsOfSummerNFT.mintTokenId(to, to, randomColor, mintAmount);
}
if(remainingToMint == 0) { break; }
colorUsed[randomColor] = true;
bool breakLoop = true;
for(uint256 colorIndex;colorIndex < 6;) {
breakLoop = breakLoop && colorUsed[colorIndex];
unchecked { ++colorIndex; }
}
if(breakLoop) { break; }
}
}
}
/**
* @notice Locks metadata for supplied list of tokens.
* Can only be called by the owner of the tokens.
* @param tokenIds array of tokens to lock metadata
*/
function lockMetadata(uint256[] calldata tokenIds) external {
for(uint256 tokenIdIndex;tokenIdIndex < tokenIds.length;) {
if(ownerOf(tokenIds[tokenIdIndex]) != msg.sender) revert NotAuthorized();
ILayerrsOfSummerRenderer(_getRenderer()).lockMetadata(tokenIds[tokenIdIndex]);
unchecked { ++tokenIdIndex; }
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
/**
* @dev EIP712 Domain for signature verification
*/
struct EIP712Domain {
string name;
string version;
uint256 chainId;
address verifyingContract;
}
/**
* @dev MintOrders contain MintParameters as defined by a token creator
* along with proofs required to validate the MintParameters and
* parameters specific to the mint being performed.
*
* `mintParameters` are the parameters signed by the token creator
* `quantity` is a multiplier for mintTokens, burnTokens and paymentTokens
* defined in mintParameters
* `mintParametersSignature` is the signature from the token creator
* `oracleSignature` is a signature of the hash of the mintParameters digest
* and msg.sender. The recovered signer must be an allowed oracle for
* the token contract if oracleSignatureRequired is true for mintParameters.
* `merkleProof` is the proof that is checked if merkleRoot is not bytes(0) in
* mintParameters
* `suppliedBurnTokenIds` is an array of tokenIds to be used when processing
* burnTokens. There must be one item in the array for each ERC1155 burnToken
* regardless of `quantity` and `quantity` items in the array for each ERC721
* burnToken.
* `referrer` is the address that will receive a portion of a paymentToken if
* not address(0) and paymentToken's referralBPS is greater than 0
* `vaultWallet` is used for allowlist mints if the msg.sender address it not on
* the allowlist but their delegate.cash vault wallet is.
*
*/
struct MintOrder {
MintParameters mintParameters;
uint256 quantity;
bytes mintParametersSignature;
bytes oracleSignature;
bytes32[] merkleProof;
uint256[] suppliedBurnTokenIds;
address referrer;
address vaultWallet;
}
/**
* @dev MintParameters define the tokens to be minted and conditions that must be met
* for the mint to be successfully processed.
*
* `mintTokens` is an array of tokens that will be minted
* `burnTokens` is an array of tokens required to be burned
* `paymentTokens` is an array of tokens required as payment
* `startTime` is the UTC timestamp of when the mint will start
* `endTime` is the UTC timestamp of when the mint will end
* `signatureMaxUses` limits the number of mints that can be performed with the
* specific mintParameters/signature
* `merkleRoot` is the root of the merkletree for allowlist minting
* `nonce` is the signer nonce that can be incremented on the LayerrMinter
* contract to invalidate all previous signatures
* `oracleSignatureRequired` if true requires a secondary signature to process the mint
*/
struct MintParameters {
MintToken[] mintTokens;
BurnToken[] burnTokens;
PaymentToken[] paymentTokens;
uint256 startTime;
uint256 endTime;
uint256 signatureMaxUses;
bytes32 merkleRoot;
uint256 nonce;
bool oracleSignatureRequired;
}
/**
* @dev Defines the token that will be minted
*
* `contractAddress` address of contract to mint tokens from
* `specificTokenId` used for ERC721 -
* if true, mint is non-sequential ERC721
* if false, mint is sequential ERC721A
* `tokenType` is the type of token being minted defined in TokenTypes.sol
* `tokenId` the tokenId to mint if specificTokenId is true
* `mintAmount` is the quantity to be minted
* `maxSupply` is checked against the total minted amount at time of mint
* minting reverts if `mintAmount` * `quantity` will cause total minted to
* exceed `maxSupply`
* `maxMintPerWallet` is checked against the number minted for the wallet
* minting reverts if `mintAmount` * `quantity` will cause wallet minted to
* exceed `maxMintPerWallet`
*/
struct MintToken {
address contractAddress;
bool specificTokenId;
uint256 tokenType;
uint256 tokenId;
uint256 mintAmount;
uint256 maxSupply;
uint256 maxMintPerWallet;
}
/**
* @dev Defines the token that will be burned
*
* `contractAddress` address of contract to burn tokens from
* `specificTokenId` specifies if the user has the option of choosing any token
* from the contract or if they must burn a specific token
* `tokenType` is the type of token being burned, defined in TokenTypes.sol
* `burnType` is the type of burn to perform, burn function call or transfer to
* dead address, defined in BurnType.sol
* `tokenId` the tokenId to burn if specificTokenId is true
* `burnAmount` is the quantity to be burned
*/
struct BurnToken {
address contractAddress;
bool specificTokenId;
uint256 tokenType;
uint256 burnType;
uint256 tokenId;
uint256 burnAmount;
}
/**
* @dev Defines the token that will be used for payment
*
* `contractAddress` address of contract to for payment if ERC20
* if tokenType is native token then this should be set to 0x000...000
* to save calldata gas units
* `tokenType` is the type of token being used for payment, defined in TokenTypes.sol
* `payTo` the address that will receive the payment
* `paymentAmount` the amount for the payment in base units for the token
* ex. a native payment on Ethereum for 1 ETH would be specified in wei
* which would be 1**18 wei
* `referralBPS` is the percentage of the payment in BPS that will be sent to the
* `referrer` on the MintOrder if `referralBPS` is greater than 0 and `referrer`
* is not address(0)
*/
struct PaymentToken {
address contractAddress;
uint256 tokenType;
address payTo;
uint256 paymentAmount;
uint256 referralBPS;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol";
import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS} from "./lib/Constants.sol";
/**
* @title OperatorFilterer
* @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
* registrant's entries in the OperatorFilterRegistry.
* @dev This smart contract is meant to be inherited by token contracts so they can use the following:
* - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
* - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
* Please note that if your token contract does not provide an owner with EIP-173, it must provide
* administration methods on the contract itself to interact with the registry otherwise the subscription
* will be locked to the options set during construction.
*/
abstract contract OperatorFilterer {
/// @dev Emitted when an operator is not allowed.
error OperatorNotAllowed(address operator);
IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
IOperatorFilterRegistry(CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS);
/// @dev The constructor that is called when the contract is being deployed.
constructor(address subscriptionOrRegistrantToCopy, bool subscribe) {
// If an inheriting token contract is deployed to a network without the registry deployed, the modifier
// will not revert, but the contract will need to be registered with the registry once it is deployed in
// order for the modifier to filter addresses.
if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
if (subscribe) {
OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
} else {
if (subscriptionOrRegistrantToCopy != address(0)) {
OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
} else {
OPERATOR_FILTER_REGISTRY.register(address(this));
}
}
}
}
/**
* @dev A helper function to check if an operator is allowed.
*/
modifier onlyAllowedOperator(address from) virtual {
// Allow spending tokens from addresses with balance
// Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
// from an EOA.
if (from != msg.sender) {
_checkFilterOperator(msg.sender);
}
_;
}
/**
* @dev A helper function to check if an operator approval is allowed.
*/
modifier onlyAllowedOperatorApproval(address operator) virtual {
_checkFilterOperator(operator);
_;
}
/**
* @dev A helper function to check if an operator is allowed.
*/
function _checkFilterOperator(address operator) internal view virtual {
// Check registry code length to facilitate testing in environments without a deployed registry.
if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
// under normal circumstances, this function will revert rather than return false, but inheriting contracts
// may specify their own OperatorFilterRegistry implementations, which may behave differently
if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
revert OperatorNotAllowed(operator);
}
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
/// @dev Simple struct to store a string value in a custom storage slot
struct StringValue {
string value;
}
/// @dev Simple struct to store an address value in a custom storage slot
struct AddressValue {
address value;
}
{
"compilationTarget": {
"contracts/LayerrsOfSummer/LayerrsOfSummer.sol": "LayerrsOfSummer"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 33333
},
"remappings": [],
"viaIR": true
}
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_extension","type":"address"},{"internalType":"address","name":"_renderer","type":"address"},{"internalType":"address[]","name":"_signers","type":"address[]"},{"internalType":"address","name":"_colorsOfSummerNFT","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"NotContractOwner","type":"error"},{"inputs":[],"name":"NotValidMintingExtension","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorNotAllowed","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"RoyaltyOverflow","type":"error"},{"inputs":[],"name":"RoyaltyReceiverIsZeroAddress","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"WithdrawFailed","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":false,"internalType":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","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":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ConsecutiveTransfer","type":"event"},{"anonymous":false,"inputs":[],"name":"LayerrContractDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"mintExtension","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"MintExtensionUpdated","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":false,"internalType":"address","name":"renderer","type":"address"}],"name":"RendererUpdated","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":"OPERATOR_FILTER_REGISTRY","outputs":[{"internalType":"contract IOperatorFilterRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"burnBatchTokenIds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burnTokenId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"colorsOfSummerNFT","outputs":[{"internalType":"contract ILayerr1155","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"name":"editContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"lockMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mintSequential","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"mintingExtensions","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"removeOperatorFilter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renderer","outputs":[{"internalType":"address","name":"_renderer","type":"address"}],"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":"payable","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":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_extension","type":"address"},{"internalType":"address","name":"_oracle","type":"address"},{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setContractAllowedOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_extension","type":"address"},{"internalType":"address","name":"_signer","type":"address"},{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setContractAllowedSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_extension","type":"address"},{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setMintExtension","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operatorFilterRegistry","type":"address"}],"name":"setOperatorFilter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_renderer","type":"address"}],"name":"setRenderer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"pct","type":"uint96"},{"internalType":"address","name":"royaltyReciever","type":"address"}],"name":"setRoyalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_extension","type":"address"},{"internalType":"bytes32[]","name":"signatureDigests","type":"bytes32[]"},{"internalType":"bool","name":"invalid","type":"bool"}],"name":"setSignatureValidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"totalMintedCollectionAndMinter","outputs":[{"internalType":"uint256","name":"totalMinted","type":"uint256"},{"internalType":"uint256","name":"minterMinted","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","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":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateMetadataAllTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"updateMetadataSpecificTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]