Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@limitbreak/creator-token-contracts/contracts/access/OwnableBasic.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./OwnablePermissions.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
abstract contract OwnableBasic is OwnablePermissions, Ownable {
function _requireCallerIsContractOwner() internal view virtual override {
_checkOwner();
}
}
"
},
"@limitbreak/creator-token-contracts/contracts/access/OwnablePermissions.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/utils/Context.sol";
abstract contract OwnablePermissions is Context {
function _requireCallerIsContractOwner() internal view virtual;
}
"
},
"@limitbreak/creator-token-contracts/contracts/erc721c/ERC721AC.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "../utils/CreatorTokenBase.sol";
import "erc721a/contracts/ERC721A.sol";
/**
* @title ERC721AC
* @author Limit Break, Inc.
* @notice Extends Azuki's ERC721-A implementation with Creator Token functionality, which
* allows the contract owner to update the transfer validation logic by managing a security policy in
* an external transfer validation security policy registry. See {CreatorTokenTransferValidator}.
*/
abstract contract ERC721AC is ERC721A, CreatorTokenBase {
constructor(string memory name_, string memory symbol_) CreatorTokenBase() ERC721A(name_, symbol_) {}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(ICreatorToken).interfaceId || super.supportsInterface(interfaceId);
}
/// @dev Ties the erc721a _beforeTokenTransfers hook to more granular transfer validation logic
function _beforeTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual override {
for (uint256 i = 0; i < quantity;) {
_validateBeforeTransfer(from, to, startTokenId + i);
unchecked {
++i;
}
}
}
/// @dev Ties the erc721a _afterTokenTransfer hook to more granular transfer validation logic
function _afterTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual override {
for (uint256 i = 0; i < quantity;) {
_validateAfterTransfer(from, to, startTokenId + i);
unchecked {
++i;
}
}
}
function _msgSenderERC721A() internal view virtual override returns (address) {
return _msgSender();
}
}
"
},
"@limitbreak/creator-token-contracts/contracts/interfaces/ICreatorToken.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "../interfaces/ICreatorTokenTransferValidator.sol";
interface ICreatorToken {
event TransferValidatorUpdated(address oldValidator, address newValidator);
function getTransferValidator() external view returns (ICreatorTokenTransferValidator);
function getSecurityPolicy() external view returns (CollectionSecurityPolicy memory);
function getWhitelistedOperators() external view returns (address[] memory);
function getPermittedContractReceivers() external view returns (address[] memory);
function isOperatorWhitelisted(address operator) external view returns (bool);
function isContractReceiverPermitted(address receiver) external view returns (bool);
function isTransferAllowed(address caller, address from, address to) external view returns (bool);
}
"
},
"@limitbreak/creator-token-contracts/contracts/interfaces/ICreatorTokenTransferValidator.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./IEOARegistry.sol";
import "./ITransferSecurityRegistry.sol";
import "./ITransferValidator.sol";
interface ICreatorTokenTransferValidator is ITransferSecurityRegistry, ITransferValidator, IEOARegistry {}"
},
"@limitbreak/creator-token-contracts/contracts/interfaces/IEOARegistry.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
interface IEOARegistry is IERC165 {
function isVerifiedEOA(address account) external view returns (bool);
}"
},
"@limitbreak/creator-token-contracts/contracts/interfaces/ITransferSecurityRegistry.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "../utils/TransferPolicy.sol";
interface ITransferSecurityRegistry {
event AddedToAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);
event CreatedAllowlist(AllowlistTypes indexed kind, uint256 indexed id, string indexed name);
event ReassignedAllowlistOwnership(AllowlistTypes indexed kind, uint256 indexed id, address indexed newOwner);
event RemovedFromAllowlist(AllowlistTypes indexed kind, uint256 indexed id, address indexed account);
event SetAllowlist(AllowlistTypes indexed kind, address indexed collection, uint120 indexed id);
event SetTransferSecurityLevel(address indexed collection, TransferSecurityLevels level);
function createOperatorWhitelist(string calldata name) external returns (uint120);
function createPermittedContractReceiverAllowlist(string calldata name) external returns (uint120);
function reassignOwnershipOfOperatorWhitelist(uint120 id, address newOwner) external;
function reassignOwnershipOfPermittedContractReceiverAllowlist(uint120 id, address newOwner) external;
function renounceOwnershipOfOperatorWhitelist(uint120 id) external;
function renounceOwnershipOfPermittedContractReceiverAllowlist(uint120 id) external;
function setTransferSecurityLevelOfCollection(address collection, TransferSecurityLevels level) external;
function setOperatorWhitelistOfCollection(address collection, uint120 id) external;
function setPermittedContractReceiverAllowlistOfCollection(address collection, uint120 id) external;
function addOperatorToWhitelist(uint120 id, address operator) external;
function addPermittedContractReceiverToAllowlist(uint120 id, address receiver) external;
function removeOperatorFromWhitelist(uint120 id, address operator) external;
function removePermittedContractReceiverFromAllowlist(uint120 id, address receiver) external;
function getCollectionSecurityPolicy(address collection) external view returns (CollectionSecurityPolicy memory);
function getWhitelistedOperators(uint120 id) external view returns (address[] memory);
function getPermittedContractReceivers(uint120 id) external view returns (address[] memory);
function isOperatorWhitelisted(uint120 id, address operator) external view returns (bool);
function isContractReceiverPermitted(uint120 id, address receiver) external view returns (bool);
}"
},
"@limitbreak/creator-token-contracts/contracts/interfaces/ITransferValidator.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "../utils/TransferPolicy.sol";
interface ITransferValidator {
function applyCollectionTransferPolicy(address caller, address from, address to) external view;
}"
},
"@limitbreak/creator-token-contracts/contracts/utils/CreatorTokenBase.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "../access/OwnablePermissions.sol";
import "../interfaces/ICreatorToken.sol";
import "../interfaces/ICreatorTokenTransferValidator.sol";
import "../utils/TransferValidation.sol";
import "@openzeppelin/contracts/interfaces/IERC165.sol";
/**
* @title CreatorTokenBase
* @author Limit Break, Inc.
* @notice CreatorTokenBase is an abstract contract that provides basic functionality for managing token
* transfer policies through an implementation of ICreatorTokenTransferValidator. This contract is intended to be used
* as a base for creator-specific token contracts, enabling customizable transfer restrictions and security policies.
*
* <h4>Features:</h4>
* <ul>Ownable: This contract can have an owner who can set and update the transfer validator.</ul>
* <ul>TransferValidation: Implements the basic token transfer validation interface.</ul>
* <ul>ICreatorToken: Implements the interface for creator tokens, providing view functions for token security policies.</ul>
*
* <h4>Benefits:</h4>
* <ul>Provides a flexible and modular way to implement custom token transfer restrictions and security policies.</ul>
* <ul>Allows creators to enforce policies such as whitelisted operators and permitted contract receivers.</ul>
* <ul>Can be easily integrated into other token contracts as a base contract.</ul>
*
* <h4>Intended Usage:</h4>
* <ul>Use as a base contract for creator token implementations that require advanced transfer restrictions and
* security policies.</ul>
* <ul>Set and update the ICreatorTokenTransferValidator implementation contract to enforce desired policies for the
* creator token.</ul>
*/
abstract contract CreatorTokenBase is OwnablePermissions, TransferValidation, ICreatorToken {
error CreatorTokenBase__InvalidTransferValidatorContract();
error CreatorTokenBase__SetTransferValidatorFirst();
address public constant DEFAULT_TRANSFER_VALIDATOR = address(0x0000721C310194CcfC01E523fc93C9cCcFa2A0Ac);
TransferSecurityLevels public constant DEFAULT_TRANSFER_SECURITY_LEVEL = TransferSecurityLevels.One;
uint120 public constant DEFAULT_OPERATOR_WHITELIST_ID = uint120(1);
ICreatorTokenTransferValidator private transferValidator;
/**
* @notice Allows the contract owner to set the transfer validator to the official validator contract
* and set the security policy to the recommended default settings.
* @dev May be overridden to change the default behavior of an individual collection.
*/
function setToDefaultSecurityPolicy() public virtual {
_requireCallerIsContractOwner();
setTransferValidator(DEFAULT_TRANSFER_VALIDATOR);
ICreatorTokenTransferValidator(DEFAULT_TRANSFER_VALIDATOR).setTransferSecurityLevelOfCollection(address(this), DEFAULT_TRANSFER_SECURITY_LEVEL);
ICreatorTokenTransferValidator(DEFAULT_TRANSFER_VALIDATOR).setOperatorWhitelistOfCollection(address(this), DEFAULT_OPERATOR_WHITELIST_ID);
}
/**
* @notice Allows the contract owner to set the transfer validator to a custom validator contract
* and set the security policy to their own custom settings.
*/
function setToCustomValidatorAndSecurityPolicy(
address validator,
TransferSecurityLevels level,
uint120 operatorWhitelistId,
uint120 permittedContractReceiversAllowlistId) public {
_requireCallerIsContractOwner();
setTransferValidator(validator);
ICreatorTokenTransferValidator(validator).
setTransferSecurityLevelOfCollection(address(this), level);
ICreatorTokenTransferValidator(validator).
setOperatorWhitelistOfCollection(address(this), operatorWhitelistId);
ICreatorTokenTransferValidator(validator).
setPermittedContractReceiverAllowlistOfCollection(address(this), permittedContractReceiversAllowlistId);
}
/**
* @notice Allows the contract owner to set the security policy to their own custom settings.
* @dev Reverts if the transfer validator has not been set.
*/
function setToCustomSecurityPolicy(
TransferSecurityLevels level,
uint120 operatorWhitelistId,
uint120 permittedContractReceiversAllowlistId) public {
_requireCallerIsContractOwner();
ICreatorTokenTransferValidator validator = getTransferValidator();
if (address(validator) == address(0)) {
revert CreatorTokenBase__SetTransferValidatorFirst();
}
validator.setTransferSecurityLevelOfCollection(address(this), level);
validator.setOperatorWhitelistOfCollection(address(this), operatorWhitelistId);
validator.setPermittedContractReceiverAllowlistOfCollection(address(this), permittedContractReceiversAllowlistId);
}
/**
* @notice Sets the transfer validator for the token contract.
*
* @dev Throws when provided validator contract is not the zero address and doesn't support
* the ICreatorTokenTransferValidator interface.
* @dev Throws when the caller is not the contract owner.
*
* @dev <h4>Postconditions:</h4>
* 1. The transferValidator address is updated.
* 2. The `TransferValidatorUpdated` event is emitted.
*
* @param transferValidator_ The address of the transfer validator contract.
*/
function setTransferValidator(address transferValidator_) public {
_requireCallerIsContractOwner();
bool isValidTransferValidator = false;
if(transferValidator_.code.length > 0) {
try IERC165(transferValidator_).supportsInterface(type(ICreatorTokenTransferValidator).interfaceId)
returns (bool supportsInterface) {
isValidTransferValidator = supportsInterface;
} catch {}
}
if(transferValidator_ != address(0) && !isValidTransferValidator) {
revert CreatorTokenBase__InvalidTransferValidatorContract();
}
emit TransferValidatorUpdated(address(transferValidator), transferValidator_);
transferValidator = ICreatorTokenTransferValidator(transferValidator_);
}
/**
* @notice Returns the transfer validator contract address for this token contract.
*/
function getTransferValidator() public view override returns (ICreatorTokenTransferValidator) {
return transferValidator;
}
/**
* @notice Returns the security policy for this token contract, which includes:
* Transfer security level, operator whitelist id, permitted contract receiver allowlist id.
*/
function getSecurityPolicy() public view override returns (CollectionSecurityPolicy memory) {
if (address(transferValidator) != address(0)) {
return transferValidator.getCollectionSecurityPolicy(address(this));
}
return CollectionSecurityPolicy({
transferSecurityLevel: TransferSecurityLevels.Zero,
operatorWhitelistId: 0,
permittedContractReceiversId: 0
});
}
/**
* @notice Returns the list of all whitelisted operators for this token contract.
* @dev This can be an expensive call and should only be used in view-only functions.
*/
function getWhitelistedOperators() public view override returns (address[] memory) {
if (address(transferValidator) != address(0)) {
return transferValidator.getWhitelistedOperators(
transferValidator.getCollectionSecurityPolicy(address(this)).operatorWhitelistId);
}
return new address[](0);
}
/**
* @notice Returns the list of permitted contract receivers for this token contract.
* @dev This can be an expensive call and should only be used in view-only functions.
*/
function getPermittedContractReceivers() public view override returns (address[] memory) {
if (address(transferValidator) != address(0)) {
return transferValidator.getPermittedContractReceivers(
transferValidator.getCollectionSecurityPolicy(address(this)).permittedContractReceiversId);
}
return new address[](0);
}
/**
* @notice Checks if an operator is whitelisted for this token contract.
* @param operator The address of the operator to check.
*/
function isOperatorWhitelisted(address operator) public view override returns (bool) {
if (address(transferValidator) != address(0)) {
return transferValidator.isOperatorWhitelisted(
transferValidator.getCollectionSecurityPolicy(address(this)).operatorWhitelistId, operator);
}
return false;
}
/**
* @notice Checks if a contract receiver is permitted for this token contract.
* @param receiver The address of the receiver to check.
*/
function isContractReceiverPermitted(address receiver) public view override returns (bool) {
if (address(transferValidator) != address(0)) {
return transferValidator.isContractReceiverPermitted(
transferValidator.getCollectionSecurityPolicy(address(this)).permittedContractReceiversId, receiver);
}
return false;
}
/**
* @notice Determines if a transfer is allowed based on the token contract's security policy. Use this function
* to simulate whether or not a transfer made by the specified `caller` from the `from` address to the `to`
* address would be allowed by this token's security policy.
*
* @notice This function only checks the security policy restrictions and does not check whether token ownership
* or approvals are in place.
*
* @param caller The address of the simulated caller.
* @param from The address of the sender.
* @param to The address of the receiver.
* @return True if the transfer is allowed, false otherwise.
*/
function isTransferAllowed(address caller, address from, address to) public view override returns (bool) {
if (address(transferValidator) != address(0)) {
try transferValidator.applyCollectionTransferPolicy(caller, from, to) {
return true;
} catch {
return false;
}
}
return true;
}
/**
* @dev Pre-validates a token transfer, reverting if the transfer is not allowed by this token's security policy.
* Inheriting contracts are responsible for overriding the _beforeTokenTransfer function, or its equivalent
* and calling _validateBeforeTransfer so that checks can be properly applied during token transfers.
*
* @dev Throws when the transfer doesn't comply with the collection's transfer policy, if the transferValidator is
* set to a non-zero address.
*
* @param caller The address of the caller.
* @param from The address of the sender.
* @param to The address of the receiver.
*/
function _preValidateTransfer(
address caller,
address from,
address to,
uint256 /*tokenId*/,
uint256 /*value*/) internal virtual override {
if (address(transferValidator) != address(0)) {
transferValidator.applyCollectionTransferPolicy(caller, from, to);
}
}
}
"
},
"@limitbreak/creator-token-contracts/contracts/utils/TransferPolicy.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
enum AllowlistTypes {
Operators,
PermittedContractReceivers
}
enum ReceiverConstraints {
None,
NoCode,
EOA
}
enum CallerConstraints {
None,
OperatorWhitelistEnableOTC,
OperatorWhitelistDisableOTC
}
enum StakerConstraints {
None,
CallerIsTxOrigin,
EOA
}
enum TransferSecurityLevels {
Zero,
One,
Two,
Three,
Four,
Five,
Six
}
struct TransferSecurityPolicy {
CallerConstraints callerConstraints;
ReceiverConstraints receiverConstraints;
}
struct CollectionSecurityPolicy {
TransferSecurityLevels transferSecurityLevel;
uint120 operatorWhitelistId;
uint120 permittedContractReceiversId;
}
"
},
"@limitbreak/creator-token-contracts/contracts/utils/TransferValidation.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/utils/Context.sol";
/**
* @title TransferValidation
* @author Limit Break, Inc.
* @notice A mix-in that can be combined with ERC-721 contracts to provide more granular hooks.
* Openzeppelin's ERC721 contract only provides hooks for before and after transfer. This allows
* developers to validate or customize transfers within the context of a mint, a burn, or a transfer.
*/
abstract contract TransferValidation is Context {
error ShouldNotMintToBurnAddress();
/// @dev Inheriting contracts should call this function in the _beforeTokenTransfer function to get more granular hooks.
function _validateBeforeTransfer(address from, address to, uint256 tokenId) internal virtual {
bool fromZeroAddress = from == address(0);
bool toZeroAddress = to == address(0);
if(fromZeroAddress && toZeroAddress) {
revert ShouldNotMintToBurnAddress();
} else if(fromZeroAddress) {
_preValidateMint(_msgSender(), to, tokenId, msg.value);
} else if(toZeroAddress) {
_preValidateBurn(_msgSender(), from, tokenId, msg.value);
} else {
_preValidateTransfer(_msgSender(), from, to, tokenId, msg.value);
}
}
/// @dev Inheriting contracts should call this function in the _afterTokenTransfer function to get more granular hooks.
function _validateAfterTransfer(address from, address to, uint256 tokenId) internal virtual {
bool fromZeroAddress = from == address(0);
bool toZeroAddress = to == address(0);
if(fromZeroAddress && toZeroAddress) {
revert ShouldNotMintToBurnAddress();
} else if(fromZeroAddress) {
_postValidateMint(_msgSender(), to, tokenId, msg.value);
} else if(toZeroAddress) {
_postValidateBurn(_msgSender(), from, tokenId, msg.value);
} else {
_postValidateTransfer(_msgSender(), from, to, tokenId, msg.value);
}
}
/// @dev Optional validation hook that fires before a mint
function _preValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a mint
function _postValidateMint(address caller, address to, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a burn
function _preValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a burn
function _postValidateBurn(address caller, address from, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires before a transfer
function _preValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}
/// @dev Optional validation hook that fires after a transfer
function _postValidateTransfer(address caller, address from, address to, uint256 tokenId, uint256 value) internal virtual {}
}
"
},
"@openzeppelin/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"@openzeppelin/contracts/interfaces/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165.sol";
"
},
"@openzeppelin/contracts/interfaces/IERC2981.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*
* _Available since v4.5._
*/
interface IERC2981 is IERC165 {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
*/
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view returns (address receiver, uint256 royaltyAmount);
}
"
},
"@openzeppelin/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
},
"@openzeppelin/contracts/utils/introspection/ERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
"
},
"@openzeppelin/contracts/utils/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
},
"contracts/CovenClash.sol": {
"content": "/*
_____ _ _____ _____
/ ____| | | ___ / ____| / ____| _
| | _ _| |_ ___ ( _ ) | | _ __ ___ ___ _ __ _ _ | | __ __ _ _ __ __ _(_)
| | | | | | __/ _ \ / _ \/\ | | | '__/ _ \/ _ \ '_ \| | | | | | |_ |/ _` | '_ \ / _` |
| |___| |_| | || __/ | (_> < | |____| | | __/ __/ |_) | |_| | | |__| | (_| | | | | (_| |_
\_____\__,_|\__\___| \___/\/ _\_____|_| \___|\___| .__/ \__, | \_____|\__,_|_| |_|\__, (_)
/ ____| / ____| | | | | | __/ | __/ |
| | _____ _____ _ __ | | | | __ _ ___| |_|_| |___/ |___/
| | / _ \ \ / / _ \ '_ \ | | | |/ _` / __| '_ \
| |___| (_) \ V / __/ | | | | |____| | (_| \__ \ | | |
\_____\___/ \_/ \___|_| |_| \_____|_|\__,_|___/_| |_|
*/
/**
* @title Smart Contract for the Cute & Creepy Gang : Coven Clash
* @notice NFT Collections with art by @Vickilicious10
*/
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@limitbreak/creator-token-contracts/contracts/access/OwnableBasic.sol";
import "@limitbreak/creator-token-contracts/contracts/erc721c/ERC721AC.sol";
import "./MutableMinterRoyalties.sol";
error InsufficientPayment();
contract CovenClash is OwnableBasic, ERC721AC, MutableMinterRoyalties {
uint256 public maxTokens = 50;
uint256 public tokenReserve = 0;
uint96 public royaltyPercent = 1000; // 10%
string public baseTokenURI;
bool public publicMintActive = false;
uint256 public publicMintPrice = 0.0111 ether;
uint256 public maxTokenPurchase = 5;
constructor()
ERC721AC("Cute & Creepy Gang: Coven Clash", "COVENCLASH")
MutableMinterRoyalties(royaltyPercent)
{}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721AC, MutableMinterRoyaltiesBase) returns (bool) {
return super.supportsInterface(interfaceId);
}
function _startTokenId() internal view virtual override returns (uint256) {
return 1;
}
function reserveTokens(address _to, uint256 _reserveAmount) external onlyOwner {
require(_reserveAmount <= tokenReserve, "Reserve exceeds limit");
require(totalSupply() + _reserveAmount <= maxTokens, "Max supply exceeded");
_mint(_to, _reserveAmount);
tokenReserve -= _reserveAmount;
}
function publicMint(uint256 _numberOfTokens) external payable {
require(publicMintActive, "Sale not active");
require(msg.sender == tx.origin, "Caller cannot be a contract");
require(_numberOfTokens <= maxTokenPurchase, "Exceeds max token purchase");
require(totalSupply() + _numberOfTokens <= maxTokens - tokenReserve, "Max supply exceeded");
uint256 cost = _numberOfTokens * publicMintPrice;
require(msg.value >= cost, "Insufficient payment");
_mint(msg.sender, _numberOfTokens);
}
function _mint(address to, uint256 quantity) internal virtual override {
uint256 nextTokenId = _nextTokenId();
for (uint256 i = 0; i < quantity;) {
_onMinted(to, nextTokenId + i);
unchecked {
++i;
}
}
super._mint(to, quantity);
}
function _baseURI() internal view virtual override returns (string memory) {
return baseTokenURI;
}
function setBaseURI(string memory baseURI) external onlyOwner {
baseTokenURI = baseURI;
}
function togglePublicMint() external onlyOwner {
publicMintActive = !publicMintActive;
}
function setPublicMintPrice(uint256 _newPrice) external onlyOwner {
publicMintPrice = _newPrice;
}
function setMaxTokenPurchase(uint256 _newMaxTokenPurchase) external onlyOwner {
maxTokenPurchase = _newMaxTokenPurchase;
}
function setTokenReserve(uint256 _newTokenReserve) external onlyOwner {
tokenReserve = _newTokenReserve;
}
function remainingSupply() external view returns (uint256) {
return maxTokens - totalSupply();
}
function setMaxSupply(uint256 _newMax) external onlyOwner {
require(maxTokens > totalSupply(), "Can't set below current");
maxTokens = _newMax;
}
function withdrawBalance() external onlyOwner {
payable(owner()).transfer(address(this).balance);
}
}"
},
"contracts/MutableMinterRoyalties.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@limitbreak/creator-token-contracts/contracts/access/OwnablePermissions.sol";
import "@openzeppelin/contracts/interfaces/IERC2981.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
/**
* @title MutableMinterRoyaltiesBase
* @author Limit Break, Inc.
* @dev Base functionality of an NFT mix-in contract implementing programmable royalties for minters, allowing the minter of each token ID to
* update the royalty fee percentage.
*/
abstract contract MutableMinterRoyaltiesBase is IERC2981, ERC165 {
error MutableMinterRoyalties__MinterCannotBeZeroAddress();
error MutableMinterRoyalties__MinterHasAlreadyBeenAssignedToTokenId();
error MutableMinterRoyalties__OnlyMinterCanChangeRoyaltyFee();
error MutableMinterRoyalties__RoyaltyFeeWillExceedSalePrice();
error MutableMinterRoyalties__RoyaltyFeeGreaterThanMaximumAllowed();
struct RoyaltyInfo {
address receiver;
uint96 royaltyFraction;
}
uint96 public constant CREATOR_MAX_ROYALTY = 2500; // Can be set to a maximum of 25%
uint96 public constant FEE_DENOMINATOR = 10_000;
uint96 private _defaultRoyaltyFeeNumerator;
mapping (uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
/// @dev Emitted when royalty is set.
event RoyaltySet(uint256 indexed tokenId, address indexed receiver, uint96 feeNumerator);
/**
* @notice Allows the minter to update the royalty fee percentage for a specific token ID.
* @dev The caller must be the minter of the specified token ID.
* @dev Throws when royaltyFeeNumerator is greater than FEE_DENOMINATOR
* @dev Throws when the caller is not the minter of the specified token ID
* @param tokenId The token ID
* @param royaltyFeeNumerator The new royalty fee numerator
*/
function setRoyaltyFee(uint256 tokenId, uint96 royaltyFeeNumerator) external {
if (royaltyFeeNumerator > CREATOR_MAX_ROYALTY) {
revert MutableMinterRoyalties__RoyaltyFeeGreaterThanMaximumAllowed();
}
RoyaltyInfo storage royalty = _tokenRoyaltyInfo[tokenId];
if (royalty.receiver != msg.sender) {
revert MutableMinterRoyalties__OnlyMinterCanChangeRoyaltyFee();
}
royalty.royaltyFraction = royaltyFeeNumerator;
emit RoyaltySet(tokenId, msg.sender, royaltyFeeNumerator);
}
/**
* @notice Indicates whether the contract implements the specified interface.
* @dev Overrides supportsInterface in ERC165.
* @param interfaceId The interface id
* @return true if the contract implements the specified interface, false otherwise
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
}
function defaultRoyaltyFeeNumerator() public virtual view returns (uint96) {
return _defaultRoyaltyFeeNumerator;
}
/**
* @notice Returns the royalty info for a given token ID and sale price.
* @dev Implements the IERC2981 interface.
* @param tokenId The token ID
* @param salePrice The sale price
* @return receiver The minter's address
* @return royaltyAmount The royalty amount
*/
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view override returns (address receiver, uint256 royaltyAmount) {
RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];
if (royalty.receiver == address(0)) {
royalty.royaltyFraction = defaultRoyaltyFeeNumerator();
}
return (royalty.receiver, (salePrice * royalty.royaltyFraction) / FEE_DENOMINATOR);
}
/**
* @dev Sets the minter's address and royalty fraction for the specified token ID in the _tokenRoyaltyInfo mapping
* when a new token is minted.
* @dev Throws when minter is the zero address
* @dev Throws when the minter has already been assigned to the specified token ID
* @param minter The address of the minter
* @param tokenId The token ID
*/
function _onMinted(address minter, uint256 tokenId) internal {
if (minter == address(0)) {
revert MutableMinterRoyalties__MinterCannotBeZeroAddress();
}
if (_tokenRoyaltyInfo[tokenId].receiver != address(0)) {
revert MutableMinterRoyalties__MinterHasAlreadyBeenAssignedToTokenId();
}
uint96 defaultRoyaltyFeeNumerator_ = defaultRoyaltyFeeNumerator();
_tokenRoyaltyInfo[tokenId] = RoyaltyInfo({
receiver: minter,
royaltyFraction: defaultRoyaltyFeeNumerator_
});
emit RoyaltySet(tokenId, minter, defaultRoyaltyFeeNumerator_);
}
/**
* @dev Removes the royalty information from the _tokenRoyaltyInfo mapping for the specified token ID when a token
* is burned.
* @param tokenId The token ID
*/
function _onBurned(uint256 tokenId) internal {
delete _tokenRoyaltyInfo[tokenId];
emit RoyaltySet(tokenId, address(0), defaultRoyaltyFeeNumerator());
}
function _setDefaultRoyaltyFee(uint96 defaultRoyaltyFeeNumerator_) internal {
if(defaultRoyaltyFeeNumerator_ > FEE_DENOMINATOR) {
revert MutableMinterRoyalties__RoyaltyFeeWillExceedSalePrice();
}
_defaultRoyaltyFeeNumerator = defaultRoyaltyFeeNumerator_;
}
}
/**
* @title MutableMinterRoyalties
* @author Limit Break, Inc.
* @notice Constructable MutableMinterRoyalties Contract implementation.
*/
abstract contract MutableMinterRoyalties is MutableMinterRoyaltiesBase {
uint96 private immutable _defaultRoyaltyFeeNumeratorImmutable;
constructor(uint96 defaultRoyaltyFeeNumerator_) {
_setDefaultRoyaltyFee(defaultRoyaltyFeeNumerator_);
_defaultRoyaltyFeeNumeratorImmutable = defaultRoyaltyFeeNumerator_;
}
function defaultRoyaltyFeeNumerator() public view override returns (uint96) {
return _defaultRoyaltyFeeNumeratorImmutable;
}
}
/**
* @title MutableMinterRoyaltiesInitializable
* @author Limit Break, Inc.
* @notice Initializable MutableMinterRoyalties Contract implementation to allow for EIP-1167 clones.
*/
abstract contract MutableMinterRoyaltiesInitializable is OwnablePermissions, MutableMinterRoyaltiesBase {
error MutableMinterRoyaltiesInitializable__DefaultMinterRoyaltyFeeAlreadyInitialized();
bool private _defaultMinterRoyaltyFeeInitialized;
function initializeDefaultMinterRoyaltyFee(uint96 defaultRoyaltyFeeNumerator_) public {
_requireCallerIsContractOwner();
if(_defaultMinterRoyaltyFeeInitialized) {
revert MutableMinterRoyaltiesInitializable__DefaultMinterRoyaltyFeeAlreadyInitialized();
}
_defaultMinterRoyaltyFeeInitialized = true;
_setDefaultRoyaltyFee(defaultRoyaltyFeeNumerator_);
}
}"
},
"erc721a/contracts/ERC721A.sol": {
"content": "// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.3.0
// 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()`.
*
* The `_sequentialUpTo()` function can be overriden to enable spot mints
* (i.e. non-consecutive mints) for `tokenId`s greater than `_sequentialUpTo()`.
*
* 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;
// The amount of tokens minted above `_sequentialUpTo()`.
// We call these spot mints (i.e. non-sequential mints).
uint256 private _spotMinted;
// =============================================================
// CONSTRUCTOR
// =============================================================
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
_currentIndex = _startTokenId();
if (_sequentialUpTo() < _startTokenId()) _revert(SequentialUpToTooSmall.selector);
}
// =============================================================
// TOKEN COUNTING OPERATIONS
// =============================================================
/**
* @dev Returns the starting token ID for sequential mints.
*
* Override this function to change the starting token ID for sequential mints.
*
* Note: The value returned must never change after any tokens have been minted.
*/
function _startTokenId() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev Returns the maximum token ID (inclusive) for sequential mints.
*
* Override this function to return a value less than 2**256 - 1,
* but greater than `_startTokenId()`, to enable spot (non-sequential) mints.
*
* Note: The value returned must never change after any tokens have been minted.
*/
function _sequentialUpTo() internal view virtual returns (uint256) {
return type(uint256).max;
}
/**
* @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 result) {
// Counter underflow is impossible as `_burnCounter` cannot be incremented
// more than `_currentIndex + _spotMinted - _startTokenId()` times.
unchecked {
// With spot minting, the intermediate `result` can be temporarily negative,
// and the computation must be unchecked.
result = _currentIndex - _burnCounter - _startTokenId();
if (_sequentialUpTo() != type(uint256).max) result += _spotMinted;
}
}
/**
* @dev Returns the total amount of tokens minted in the contract.
*/
function _totalMinted() internal view virtual returns (uint256 result) {
// Counter underflow is impossible as `_currentIndex` does not decrement,
// and it is initialized to `_startTokenId()`.
unchecked {
result = _currentIndex - _startTokenId();
if (_sequentialUpTo() != type(uint256).max) result += _spotMinted;
}
}
/**
* @dev Returns the total number of tokens burned.
*/
function _totalBurned() internal view virtual returns (uint256) {
return _burnCounter;
}
/**
* @dev Returns the total number of tokens that are spot-minted.
*/
function _totalSpotMinted() internal view virtual returns (uint256) {
return _spotMinted;
}
// =============================================================
// 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.
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);
}
}
/**
* @dev Returns the packed ownership data of `tokenId`.
*/
function _packedOwnershipOf(uint256 tokenId) private view returns (uint256 packed) {
if (_startTokenId() <= tokenId) {
packed = _packedOwnerships[tokenId];
if (tokenId > _sequentialUpTo()) {
if (_packedOwnershipExists(packed)) return packed;
_revert(OwnerQueryForNonexistentToken.selector);
}
// 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) {
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.
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 > _sequentialUpTo()) return _packedOwnershipExists(_packedOwnerships[tokenId]);
if (tokenId < _currentIndex) {
uint256 packed;
while ((packed = _packedOwnerships[tokenId]) == 0) --tokenId;
result = packed & _BITMASK_BURNED == 0;
}
}
}
/**
* @dev Returns whether `packed` represents a token that exists.
*/
function _packedOwnershipExists(uint256 packed) private pure returns (bool result) {
assembly {
// The following is equivalent to `owner != address(0) && burned == false`.
// Symbolically tested.
result := gt(and(packed, _BITMASK_ADDRESS), and(packed, _BITMASK_BURNED))
}
}
/**
* @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
*/
function _isSenderApprovedOrOwner(
address approvedAddress,
address owner,
address msgSender
) private pure returns (bool result) {
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 approv
Submitted on: 2025-10-30 11:54:13
Comments
Log in to comment.
No comments yet.