// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {IERC721Drop} from "../../interfaces/IERC721Drop.sol";
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {Base} from "./Base.sol";
/// @title Admin
/// @notice An abstract contract with access control functionality.
abstract contract Admin is Base {
/// @notice Access control roles
bytes32 public constant MINTER_ROLE = keccak256("MINTER");
/// @notice Modifier to allow only users with admin access
/// @param target The address of the contract implementing the access control
modifier onlyAdmin(address target) {
if (!isAdmin(target, msg.sender)) {
revert IERC721Drop.Access_OnlyAdmin();
}
_;
}
/// @notice Modifier to allow only a given role or admin access
/// @param target The address of the contract implementing the access control
/// @param role The role to check for alongside the admin role
modifier onlyRoleOrAdmin(address target, bytes32 role) {
if (!isAdmin(target, msg.sender) && !IAccessControl(target).hasRole(role, msg.sender)) {
revert IERC721Drop.Access_MissingRoleOrAdmin(role);
}
_;
}
/// @notice Getter for admin role associated with the contract to handle minting
/// @param target The address of the contract implementing the access control
/// @param user The address to check for admin access
/// @return Whether the address has admin access or not
function isAdmin(address target, address user) public view returns (bool) {
return IERC721Drop(target).isAdmin(user);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
/// @title Base
/// @notice A base abstract contract implementing common functionality for other contracts.
abstract contract Base {
/// @notice given address is invalid.
error AddressCannotBeZero();
/// @dev Modifier to check if the provided address is not the zero address.
/// @param addr The address to be checked.
modifier notZeroAddress(address addr) {
if (addr == address(0)) {
revert AddressCannotBeZero();
}
_;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {IERC6551Registry} from "lib/ERC6551/src/interfaces/IERC6551Registry.sol";
/**
██████╗██████╗ ███████╗ █████╗ ██████╗ ██████╗ ███████╗
██╔════╝██╔══██╗██╔════╝██╔══██╗██╔═══██╗██╔══██╗██╔════╝
██║ ██████╔╝█████╗ ╚█████╔╝██║ ██║██████╔╝███████╗
██║ ██╔══██╗██╔══╝ ██╔══██╗██║ ██║██╔══██╗╚════██║
╚██████╗██║ ██║███████╗╚█████╔╝╚██████╔╝██║ ██║███████║
╚═════╝╚═╝ ╚═╝╚══════╝ ╚════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
*/
/// @title Cre8orsERC6551 contract for handling ERC6551 token-bound accounts.
/// @dev inspiration: https://github.com/ourzora/zora-drops-contracts
contract Cre8orsERC6551 {
/// @notice The address of ERC6551 Registry contract.
address public erc6551Registry;
/// @notice The address of ERC6551 Account Implementation contract.
address public erc6551AccountImplementation;
/// @notice Initializes the Cre8orsERC6551 contract with ERC6551 Registry and Implementation addresses.
/// @param _registry The address of the ERC6551 registry contract.
/// @param _implementation The address of the ERC6551 account implementation contract.
constructor(address _registry, address _implementation) {
erc6551Registry = _registry;
erc6551AccountImplementation = _implementation;
}
/// @dev Initial data for ERC6551 createAccount function.
bytes public constant INIT_DATA = "0x8129fc1c";
/// @notice Creates Token Bound Accounts (TBA) with ERC6551.
/// @dev Internal function used to create TBAs for a given ERC721 contract.
/// @param _target Target ERC721 contract address.
/// @param startTokenId Token ID to start from.
/// @param quantity Number of token-bound accounts to create.
/// @return airdropList An array containing the addresses of the created TBAs.
function createTokenBoundAccounts(
address _target,
uint256 startTokenId,
uint256 quantity
) internal returns (address[] memory airdropList) {
IERC6551Registry registry = IERC6551Registry(erc6551Registry);
airdropList = new address[](quantity);
for (uint256 i = 0; i < quantity; i++) {
address smartWallet = registry.createAccount(
erc6551AccountImplementation,
block.chainid,
_target,
startTokenId + i,
0,
INIT_DATA
);
airdropList[i] = smartWallet;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {Cre8orsERC6551} from "../utils/Cre8orsERC6551.sol";
import {ICre8ors} from "../interfaces/ICre8ors.sol";
import {ICre8ing} from "../interfaces/ICre8ing.sol";
import {IERC721Drop} from "../interfaces/IERC721Drop.sol";
import {ISubscription} from "../subscription/interfaces/ISubscription.sol";
import {Admin} from "../subscription/abstracts/Admin.sol";
import {IERC6551Registry} from "lib/ERC6551/src/interfaces/IERC6551Registry.sol";
contract DNAMinter is Cre8orsERC6551, Admin {
/// @notice Error event for attempting to mint a DNA Card that has already been minted.
error DNAMinter_AlreadyMinted();
/// @notice The address of the collection contract for Cre8ors.
address public cre8orsNft;
/// @notice The address of the collection contract for Cre8ors DNA cards.
address public dnaNft;
/// @notice Initializes the contract with the address of the Cre8orsNFT contract.
/// @param _cre8orsNft The address of the Cre8ors contract to be used.
/// @param _dnaNft The address of the Cre8ors DNA contract to be used.
/// @param _registry The address of the ERC6551 registry contract to be used.
/// @param _implementation The address of the ERC6551 implementation contract to be used.
constructor(
address _cre8orsNft,
address _dnaNft,
address _registry,
address _implementation
) Cre8orsERC6551(_registry, _implementation) {
cre8orsNft = _cre8orsNft;
dnaNft = _dnaNft;
}
/// @notice Creates a Token Bound Account (TBA) and mints a DNA NFT.
/// @param _cre8orsTokenId Token ID of the Cre8ors NFT for which DNA will be minted.
/// @return _mintedDnaTokenId ID of the minted DNA token.
function createTokenBoundAccountAndMintDNA(
uint256 _cre8orsTokenId
)
public
onlyFirstMint(_cre8orsTokenId)
returns (uint256 _mintedDnaTokenId)
{
address[] memory airdropList = createTokenBoundAccounts(
cre8orsNft,
_cre8orsTokenId,
1
);
_mintedDnaTokenId = ICre8ors(dnaNft).adminMint(airdropList[0], 1);
}
/// @notice Modifier to allow minting only if it is the first mint for the given Cre8ors token ID.
/// @param _cre8orsTokenId Token ID to check for first minting.
modifier onlyFirstMint(uint256 _cre8orsTokenId) {
address tba = IERC6551Registry(erc6551Registry).account(
erc6551AccountImplementation,
block.chainid,
cre8orsNft,
_cre8orsTokenId,
0
);
if (ICre8ors(dnaNft).mintedPerAddress(tba).totalMints > 0) {
revert DNAMinter_AlreadyMinted();
}
_;
}
/// @notice Set the Cre8orsNFT contract address.
/// @dev This function can only be called by an admin, identified by the
/// "cre8orsNFT" contract address.
/// @param _dnaNft The new address of the DNA contract to be set.
function setDnaNFT(address _dnaNft) public onlyAdmin(dnaNft) {
dnaNft = _dnaNft;
}
/// @notice Set the ERC6551 registry address.
/// @dev This function can only be called by an admin.
/// @param _registry Address of the ERC6551 registry to be set.
function setErc6551Registry(address _registry) public onlyAdmin(dnaNft) {
erc6551Registry = _registry;
}
/// @notice Set the ERC6551 account implementation address.
/// @dev This function can only be called by an admin.
/// @param _implementation Address of the ERC6551 account implementation to be set.
function setErc6551Implementation(
address _implementation
) public onlyAdmin(dnaNft) {
erc6551AccountImplementation = _implementation;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {ILockup} from "./ILockup.sol";
interface ICre8ing {
/// @notice Getter for Lockup interface
function lockUp(address) external view returns (ILockup);
/// @dev Emitted when a CRE8OR begins cre8ing.
event Cre8ed(address, uint256 indexed tokenId);
/// @dev Emitted when a CRE8OR stops cre8ing; either through standard means or
/// by expulsion.
event Uncre8ed(address, uint256 indexed tokenId);
/// @dev Emitted when a CRE8OR is expelled from the Warehouse.
event Expelled(address, uint256 indexed tokenId);
/// @notice Missing cre8ing status
error CRE8ING_NotCre8ing(address, uint256 tokenId);
/// @notice Cre8ing Closed
error Cre8ing_Cre8ingClosed();
/// @notice Cre8ing
error Cre8ing_Cre8ing();
/// @notice Missing Lockup
error Cre8ing_MissingLockup();
/// @notice Cre8ing period
function cre8ingPeriod(
address,
uint256
) external view returns (bool cre8ing, uint256 current, uint256 total);
/// @notice open / close staking
function setCre8ingOpen(address, bool) external;
/// @notice force removal from staking
function expelFromWarehouse(address, uint256) external;
/// @notice function getCre8ingStarted(
function getCre8ingStarted(
address _target,
uint256 tokenId
) external view returns (uint256);
/// @notice array of staked tokenIDs
/// @dev used in cre8ors ui to quickly get list of staked NFTs.
function cre8ingTokens(
address _target
) external view returns (uint256[] memory stakedTokens);
/// @notice initialize both staking and lockups
function inializeStakingAndLockup(
address _target,
uint256[] memory,
bytes memory
) external;
/// @notice Set a new lockup for the target.
/// @param _target The target address.
/// @param newLockup The new lockup contract address.
function setLockup(address _target, ILockup newLockup) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {IERC721Drop} from "./IERC721Drop.sol";
import {ILockup} from "./ILockup.sol";
import {IERC721A} from "erc721a/contracts/IERC721A.sol";
import {ICre8ing} from "./ICre8ing.sol";
import {ISubscription} from "../subscription/interfaces/ISubscription.sol";
/**
██████╗██████╗ ███████╗ █████╗ ██████╗ ██████╗ ███████╗
██╔════╝██╔══██╗██╔════╝██╔══██╗██╔═══██╗██╔══██╗██╔════╝
██║ ██████╔╝█████╗ ╚█████╔╝██║ ██║██████╔╝███████╗
██║ ██╔══██╗██╔══╝ ██╔══██╗██║ ██║██╔══██╗╚════██║
╚██████╗██║ ██║███████╗╚█████╔╝╚██████╔╝██║ ██║███████║
╚═════╝╚═╝ ╚═╝╚══════╝ ╚════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
*/
/// @notice Interface for Cre8ors Drops contract
interface ICre8ors is IERC721Drop, IERC721A {
function cre8ing() external view returns (ICre8ing);
/// @notice Getter for last minted token ID (gets next token id and subtracts 1)
function _lastMintedTokenId() external view returns (uint256);
/// @dev Returns `true` if `account` has been granted `role`.
function hasRole(
bytes32 role,
address account
) external view returns (bool);
function subscription() external view returns (address);
function setSubscription(address newSubscription) external;
function setCre8ing(ICre8ing _cre8ing) external;
function MINTER_ROLE() external returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC6551Registry {
event AccountCreated(
address account,
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
);
function createAccount(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 seed,
bytes calldata initData
) external returns (address);
function account(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
) external view returns (address);
}
// 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();
// =============================================================
// 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.15;
import {IMetadataRenderer} from "../interfaces/IMetadataRenderer.sol";
/**
██████╗██████╗ ███████╗ █████╗ ██████╗ ██████╗ ███████╗
██╔════╝██╔══██╗██╔════╝██╔══██╗██╔═══██╗██╔══██╗██╔════╝
██║ ██████╔╝█████╗ ╚█████╔╝██║ ██║██████╔╝███████╗
██║ ██╔══██╗██╔══╝ ██╔══██╗██║ ██║██╔══██╗╚════██║
╚██████╗██║ ██║███████╗╚█████╔╝╚██████╔╝██║ ██║███████║
╚═════╝╚═╝ ╚═╝╚══════╝ ╚════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
*/
/// @notice Interface for Cre8ors Drop contract
interface IERC721Drop {
// Access errors
/// @notice Only admin can access this function
error Access_OnlyAdmin();
/// @notice Missing the given role or admin access
error Access_MissingRoleOrAdmin(bytes32 role);
/// @notice Withdraw is not allowed by this user
error Access_WithdrawNotAllowed();
/// @notice Cannot withdraw funds due to ETH send failure.
error Withdraw_FundsSendFailure();
/// @notice Missing the owner role.
error Access_OnlyOwner();
/// @notice Missing the owner role or approved nft access.
error Access_MissingOwnerOrApproved();
// Sale/Purchase errors
/// @notice Sale is inactive
error Sale_Inactive();
/// @notice Presale is inactive
error Presale_Inactive();
/// @notice Presale merkle root is invalid
error Presale_MerkleNotApproved();
/// @notice Wrong price for purchase
error Purchase_WrongPrice(uint256 correctPrice);
/// @notice NFT sold out
error Mint_SoldOut();
/// @notice Too many purchase for address
error Purchase_TooManyForAddress();
/// @notice Too many presale for address
error Presale_TooManyForAddress();
// Admin errors
/// @notice Royalty percentage too high
error Setup_RoyaltyPercentageTooHigh(uint16 maxRoyaltyBPS);
/// @notice Invalid admin upgrade address
error Admin_InvalidUpgradeAddress(address proposedAddress);
/// @notice Unable to finalize an edition not marked as open (size set to uint64_max_value)
error Admin_UnableToFinalizeNotOpenEdition();
/// @notice Event emitted for each sale
/// @param to address sale was made to
/// @param quantity quantity of the minted nfts
/// @param pricePerToken price for each token
/// @param firstPurchasedTokenId first purchased token ID (to get range add to quantity for max)
event Sale(
address indexed to,
uint256 indexed quantity,
uint256 indexed pricePerToken,
uint256 firstPurchasedTokenId
);
/// @notice Sales configuration has been changed
/// @dev To access new sales configuration, use getter function.
/// @param changedBy Changed by user
event SalesConfigChanged(address indexed changedBy);
/// @notice Event emitted when the funds recipient is changed
/// @param newAddress new address for the funds recipient
/// @param changedBy address that the recipient is changed by
event FundsRecipientChanged(
address indexed newAddress,
address indexed changedBy
);
/// @notice Event emitted when the funds are withdrawn from the minting contract
/// @param withdrawnBy address that issued the withdraw
/// @param withdrawnTo address that the funds were withdrawn to
/// @param amount amount that was withdrawn
event FundsWithdrawn(
address indexed withdrawnBy,
address indexed withdrawnTo,
uint256 amount
);
/// @notice Event emitted when an open mint is finalized and further minting is closed forever on the contract.
/// @param sender address sending close mint
/// @param numberOfMints number of mints the contract is finalized at
event OpenMintFinalized(address indexed sender, uint256 numberOfMints);
/// @notice Event emitted when metadata renderer is updated.
/// @param sender address of the updater
/// @param renderer new metadata renderer address
event UpdatedMetadataRenderer(address sender, IMetadataRenderer renderer);
/// @notice General configuration for NFT Minting and bookkeeping
struct Configuration {
/// @dev Metadata renderer (uint160)
IMetadataRenderer metadataRenderer;
/// @dev Total size of edition that can be minted (uint160+64 = 224)
uint64 editionSize;
/// @dev Royalty amount in bps (uint224+16 = 240)
uint16 royaltyBPS;
/// @dev Funds recipient for sale (new slot, uint160)
address payable fundsRecipient;
}
/// @notice Sales states and configuration
/// @dev Uses 3 storage slots
struct SalesConfiguration {
/// @dev Public sale price (max ether value > 1000 ether with this value)
uint104 publicSalePrice;
/// @dev ERC20 Token
address erc20PaymentToken;
/// @notice Purchase mint limit per address (if set to 0 === unlimited mints)
/// @dev Max purchase number per txn (90+32 = 122)
uint32 maxSalePurchasePerAddress;
/// @dev uint64 type allows for dates into 292 billion years
/// @notice Public sale start timestamp (136+64 = 186)
uint64 publicSaleStart;
/// @notice Public sale end timestamp (186+64 = 250)
uint64 publicSaleEnd;
/// @notice Presale start timestamp
/// @dev new storage slot
uint64 presaleStart;
/// @notice Presale end timestamp
uint64 presaleEnd;
/// @notice Presale merkle root
bytes32 presaleMerkleRoot;
}
/// @notice CRE8ORS - General configuration for Builder Rewards burn requirements
struct BurnConfiguration {
/// @dev Token to burn
address burnToken;
/// @dev Required number of tokens to burn
uint256 burnQuantity;
}
/// @notice Sales states and configuration
/// @dev Uses 3 storage slots
struct ERC20SalesConfiguration {
/// @notice Public sale price
/// @dev max ether value > 1000 ether with this value
uint104 publicSalePrice;
/// @dev ERC20 Token
address erc20PaymentToken;
/// @notice Purchase mint limit per address (if set to 0 === unlimited mints)
/// @dev Max purchase number per txn (90+32 = 122)
uint32 maxSalePurchasePerAddress;
/// @dev uint64 type allows for dates into 292 billion years
/// @notice Public sale start timestamp (136+64 = 186)
uint64 publicSaleStart;
/// @notice Public sale end timestamp (186+64 = 250)
uint64 publicSaleEnd;
/// @notice Presale start timestamp
/// @dev new storage slot
uint64 presaleStart;
/// @notice Presale end timestamp
uint64 presaleEnd;
/// @notice Presale merkle root
bytes32 presaleMerkleRoot;
}
/// @notice Return value for sales details to use with front-ends
struct SaleDetails {
// Synthesized status variables for sale and presale
bool publicSaleActive;
bool presaleActive;
// Price for public sale
uint256 publicSalePrice;
// Timed sale actions for public sale
uint64 publicSaleStart;
uint64 publicSaleEnd;
// Timed sale actions for presale
uint64 presaleStart;
uint64 presaleEnd;
// Merkle root (includes address, quantity, and price data for each entry)
bytes32 presaleMerkleRoot;
// Limit public sale to a specific number of mints per wallet
uint256 maxSalePurchasePerAddress;
// Information about the rest of the supply
// Total that have been minted
uint256 totalMinted;
// The total supply available
uint256 maxSupply;
}
/// @notice Return value for sales details to use with front-ends
struct ERC20SaleDetails {
/// @notice Synthesized status variables for sale
bool publicSaleActive;
/// @notice Synthesized status variables for presale
bool presaleActive;
/// @notice Price for public sale
uint256 publicSalePrice;
/// @notice ERC20 contract address for payment. address(0) for ETH.
address erc20PaymentToken;
/// @notice public sale start
uint64 publicSaleStart;
/// @notice public sale end
uint64 publicSaleEnd;
/// @notice Timed sale actions for presale start
uint64 presaleStart;
/// @notice Timed sale actions for presale end
uint64 presaleEnd;
/// @notice Merkle root (includes address, quantity, and price data for each entry)
bytes32 presaleMerkleRoot;
/// @notice Limit public sale to a specific number of mints per wallet
uint256 maxSalePurchasePerAddress;
/// @notice Total that have been minted
uint256 totalMinted;
/// @notice The total supply available
uint256 maxSupply;
}
/// @notice Return type of specific mint counts and details per address
struct AddressMintDetails {
/// Number of total mints from the given address
uint256 totalMints;
/// Number of presale mints from the given address
uint256 presaleMints;
/// Number of public mints from the given address
uint256 publicMints;
}
/// @notice External purchase function (payable in eth)
/// @param quantity to purchase
/// @return first minted token ID
function purchase(uint256 quantity) external payable returns (uint256);
/// @notice External purchase presale function (takes a merkle proof and matches to root) (payable in eth)
/// @param quantity to purchase
/// @param maxQuantity can purchase (verified by merkle root)
/// @param pricePerToken price per token allowed (verified by merkle root)
/// @param merkleProof input for merkle proof leaf verified by merkle root
/// @return first minted token ID
function purchasePresale(
uint256 quantity,
uint256 maxQuantity,
uint256 pricePerToken,
bytes32[] memory merkleProof
) external payable returns (uint256);
/// @notice Function to return the global sales details for the given drop
function saleDetails() external view returns (ERC20SaleDetails memory);
/// @notice Function to return the specific sales details for a given address
/// @param minter address for minter to return mint information for
function mintedPerAddress(
address minter
) external view returns (AddressMintDetails memory);
/// @notice This is the opensea/public owner setting that can be set by the contract admin
function owner() external view returns (address);
/// @notice Update the metadata renderer
/// @param newRenderer new address for renderer
/// @param setupRenderer data to call to bootstrap data for the new renderer (optional)
function setMetadataRenderer(
IMetadataRenderer newRenderer,
bytes memory setupRenderer
) external;
/// @notice This is an admin mint function to mint a quantity to a specific address
/// @param to address to mint to
/// @param quantity quantity to mint
/// @return the id of the first minted NFT
function adminMint(address to, uint256 quantity) external returns (uint256);
/// @notice This is an admin mint function to mint a single nft each to a list of addresses
/// @param to list of addresses to mint an NFT each to
/// @return the id of the first minted NFT
function adminMintAirdrop(address[] memory to) external returns (uint256);
/// @dev Getter for admin role associated with the contract to handle metadata
/// @return boolean if address is admin
function isAdmin(address user) external view returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
/**
██████╗██████╗ ███████╗ █████╗ ██████╗ ██████╗ ███████╗
██╔════╝██╔══██╗██╔════╝██╔══██╗██╔═══██╗██╔══██╗██╔════╝
██║ ██████╔╝█████╗ ╚█████╔╝██║ ██║██████╔╝███████╗
██║ ██╔══██╗██╔══╝ ██╔══██╗██║ ██║██╔══██╗╚════██║
╚██████╗██║ ██║███████╗╚█████╔╝╚██████╔╝██║ ██║███████║
╚═════╝╚═╝ ╚═╝╚══════╝ ╚════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
*/
interface ILockup {
/// @notice Storage for token edition information
struct TokenLockupInfo {
uint64 unlockDate;
uint256 priceToUnlock;
}
/// @notice Locked
error Lockup_Locked();
/// @notice Wrong price for unlock
error Unlock_WrongPrice(uint256 correctPrice);
/// @notice Event for updated Lockup
event TokenLockupUpdated(
address indexed target,
uint256 tokenId,
uint64 unlockDate,
uint256 priceToUnlock
);
/// @notice retrieves locked state for token
function isLocked(address, uint256) external view returns (bool);
/// @notice retieves unlock date for token
function unlockInfo(
address,
uint256
) external view returns (TokenLockupInfo memory);
/// @notice sets unlock tier for token
function setUnlockInfo(address, uint256, bytes memory) external;
/// @notice pay to unlock a locked token
function payToUnlock(address payable, uint256) external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
/**
██████╗██████╗ ███████╗ █████╗ ██████╗ ██████╗ ███████╗
██╔════╝██╔══██╗██╔════╝██╔══██╗██╔═══██╗██╔══██╗██╔════╝
██║ ██████╔╝█████╗ ╚█████╔╝██║ ██║██████╔╝███████╗
██║ ██╔══██╗██╔══╝ ██╔══██╗██║ ██║██╔══██╗╚════██║
╚██████╗██║ ██║███████╗╚█████╔╝╚██████╔╝██║ ██║███████║
╚═════╝╚═╝ ╚═╝╚══════╝ ╚════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
*/
/// @dev credit: https://github.com/ourzora/zora-drops-contracts
interface IMetadataRenderer {
function tokenURI(uint256) external view returns (string memory);
function contractURI() external view returns (string memory);
function initializeWithData(bytes memory initData) external;
}
/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
/// @title ISubscription
/// @dev Interface for managing subscriptions to NFTs.
interface ISubscription {
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @notice The subscription associated with the provided token ID is invalid or has expired.
error InvalidSubscription();
/// @notice Attempting to set a subscription contract address with a zero address value.
error SubscriptionCannotBeZeroAddress();
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/// @dev Emitted when the renewability status of subscriptions is updated.
event RenewableUpdate(bool renewable);
/// @dev Emitted when the minimum duration for subscription renewal is updated.
event MinRenewalDurationUpdate(uint64 duration);
/// @dev Emitted when the maximum duration for subscription renewal is updated.
event MaxRenewalDurationUpdate(uint64 duration);
/*//////////////////////////////////////////////////////////////
CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @notice Checks the subscription for the given `tokenId`.
/// Throws if `tokenId` subscription has expired.
/// @param tokenId The unique identifier of the NFT token.
function checkSubscription(uint256 tokenId) external view;
/// @notice Returns whether the subscription for the given `tokenId` is valid.
/// @param tokenId The unique identifier of the NFT token.
/// @return A boolean indicating if the subscription is valid.
function isSubscriptionValid(uint256 tokenId) external view returns (bool);
/*//////////////////////////////////////////////////////////////
NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////*/
/*////////// updateSubscriptionForFree variants //////////*/
/// @notice Extends the subscription for the given `tokenId` with a specified `duration` for free.
/// @dev This function is meant to be called by the minter when minting the NFT to subscribe.
/// @param target The address of the contract implementing the access control
/// @param duration The duration (in seconds) to extend the subscription for.
/// @param tokenId The unique identifier of the NFT token to be subscribed.
function updateSubscriptionForFree(address target, uint64 duration, uint256 tokenId) external;
/// @notice Extends the subscription for the given `tokenIds` with a specified `duration` for free.
/// @dev This function is meant to be called by the minter when minting the NFT to subscribe.
/// @param target The address of the contract implementing the access control
/// @param duration The duration (in seconds) to extend the subscription for.
/// @param tokenIds An array of unique identifiers of the NFT tokens to update the subscriptions for.
function updateSubscriptionForFree(address target, uint64 duration, uint256[] calldata tokenIds) external;
/*////////////// updateSubscription variants /////////////*/
/// @notice Extends the subscription for the given `tokenId` with a specified `duration`, using native currency as
/// payment.
/// @dev This function is meant to be called by the minter when minting the NFT to subscribe.
/// @param target The address of the contract implementing the access control
/// @param duration The duration (in seconds) to extend the subscription for.
/// @param tokenId The unique identifier of the NFT token to be subscribed.
function updateSubscription(address target, uint64 duration, uint256 tokenId) external payable;
/// @notice Extends the subscription for the given `tokenIds` with a specified `duration`, using native currency as
/// payment.
/// @dev This function is meant to be called by the minter when minting the NFT to subscribe.
/// @param target The address of the contract implementing the access control
/// @param duration The duration (in seconds) to extend the subscription for.
/// @param tokenIds An array of unique identifiers of the NFT tokens to update the subscriptions for.
function updateSubscription(address target, uint64 duration, uint256[] calldata tokenIds) external payable;
}
{
"compilationTarget": {
"src/minter/DNAMinter.sol": "DNAMinter"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/=lib/openzeppelin-contracts/",
":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":ERC6551/=lib/ERC6551/src/",
":ERC721A-Upgradeable/=lib/ERC721A-Upgradeable/contracts/",
":ERC721A/=lib/ERC721A/contracts/",
":ERC721C/=lib/creator-token-contracts/contracts/",
":ERC721H/=lib/ERC721H/src/",
":account-abstraction/=lib/account-abstraction/contracts/",
":creator-token-contracts/=lib/creator-token-contracts/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":erc6551/=lib/tokenbound/lib/reference/src/",
":erc721a/=lib/ERC721A/",
":forge-std/=lib/forge-std/src/",
":hardhat/=lib/creator-token-contracts/node_modules/hardhat/",
":murky/=lib/creator-token-contracts/lib/murky/src/",
":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
":reference/=lib/tokenbound/lib/reference/src/",
":sstore2/=lib/ERC6551/lib/sstore2/contracts/",
":tokenbound/=lib/tokenbound/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_cre8orsNft","type":"address"},{"internalType":"address","name":"_dnaNft","type":"address"},{"internalType":"address","name":"_registry","type":"address"},{"internalType":"address","name":"_implementation","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Access_OnlyAdmin","type":"error"},{"inputs":[],"name":"AddressCannotBeZero","type":"error"},{"inputs":[],"name":"DNAMinter_AlreadyMinted","type":"error"},{"inputs":[],"name":"INIT_DATA","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cre8orsNft","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_cre8orsTokenId","type":"uint256"}],"name":"createTokenBoundAccountAndMintDNA","outputs":[{"internalType":"uint256","name":"_mintedDnaTokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dnaNft","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"erc6551AccountImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"erc6551Registry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"address","name":"user","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_dnaNft","type":"address"}],"name":"setDnaNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_implementation","type":"address"}],"name":"setErc6551Implementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_registry","type":"address"}],"name":"setErc6551Registry","outputs":[],"stateMutability":"nonpayable","type":"function"}]