Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
010c3c3
1155
wolfy-nft Dec 12, 2024
5d373e7
fix build
wolfy-nft Dec 12, 2024
e98e6e4
remove test
wolfy-nft Dec 12, 2024
7a4aadc
Merge branch 'adam/feat/selfserve' of https://github.com/wolfy-nft/ma…
wolfy-nft Dec 12, 2024
2d8a8db
mint funcs
wolfy-nft Dec 13, 2024
62c20c9
fix
wolfy-nft Dec 16, 2024
99c0a83
comments
wolfy-nft Dec 17, 2024
3e06fd3
docs
wolfy-nft Dec 17, 2024
be884be
Merge branch 'adam/feat/selfserve' of https://github.com/wolfy-nft/ma…
wolfy-nft Dec 17, 2024
3fd2a47
Merge branch 'adam/feat/selfserve' of https://github.com/wolfy-nft/ma…
wolfy-nft Dec 17, 2024
bf7f194
fix
wolfy-nft Dec 17, 2024
f85d80e
fix
wolfy-nft Dec 17, 2024
dedfe08
natspec
wolfy-nft Dec 17, 2024
c6202a0
docs
wolfy-nft Dec 17, 2024
27bbe29
1155 tests
wolfy-nft Dec 17, 2024
ac16d17
initial tests
wolfy-nft Dec 17, 2024
0dbc7b0
update tests
wolfy-nft Dec 17, 2024
2f4ddef
tests
wolfy-nft Dec 17, 2024
6a07d01
tests
wolfy-nft Dec 18, 2024
ae60719
test coverage
wolfy-nft Dec 18, 2024
46686fa
tests
wolfy-nft Dec 18, 2024
bf8448e
Merge branch 'adam/feat/selfserve' of https://github.com/wolfy-nft/ma…
wolfy-nft Dec 18, 2024
4574a2b
update
wolfy-nft Dec 18, 2024
38f956a
Merge branch 'adam/feat/selfserve' of https://github.com/wolfy-nft/ma…
wolfy-nft Dec 18, 2024
01483c2
cleanup
wolfy-nft Dec 18, 2024
a309ac7
cleanup
wolfy-nft Dec 18, 2024
fdcdcff
add royalties to setup config
wolfy-nft Dec 19, 2024
6a98ecd
add config function
wolfy-nft Jan 2, 2025
2d9c3f9
emit contract updated event
wolfy-nft Jan 6, 2025
12567c8
add header
wolfy-nft Jan 7, 2025
d9d911f
adjust fee config
wolfy-nft Jan 8, 2025
285b228
add missing event
wolfy-nft Jan 14, 2025
65af906
add mint event
wolfy-nft Jan 14, 2025
94a4bd0
change order of mint
wolfy-nft Jan 14, 2025
eb888e2
update allowlist
wolfy-nft Jan 16, 2025
e449030
fmt
wolfy-nft Jan 16, 2025
d69dfbe
fix burn function for 1155
wolfy-nft Jan 20, 2025
2638278
remove unused imports
wolfy-nft Jan 20, 2025
7dbd380
fix overpayment
wolfy-nft Jan 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {ERC1155} from "solady/src/tokens/ERC1155.sol";

/// @title ERC1155ConduitPreapprovedCloneable
/// @notice ERC1155 token with the MagicEden conduit preapproved for seamless transactions.
abstract contract ERC1155ConduitPreapprovedCloneable is ERC1155 {
/// @dev The canonical MagicEden conduit address.
address internal constant _CONDUIT = 0x2052f8A2Ff46283B30084e5d84c89A2fdBE7f74b;

/// @notice Safely transfers `amount` tokens of type `id` from `from` to `to`.
/// @param from The address holding the tokens.
/// @param to The address to transfer the tokens to.
/// @param id The token type identifier.
/// @param amount The number of tokens to transfer.
/// @param data Additional data with no specified format.
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data)
public
virtual
override
{
_safeTransfer(_by(), from, to, id, amount, data);
}

/// @notice Safely transfers a batch of tokens from `from` to `to`.
/// @param from The address holding the tokens.
/// @param to The address to transfer the tokens to.
/// @param ids An array of token type identifiers.
/// @param amounts An array of amounts to transfer for each token type.
/// @param data Additional data with no specified format.
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) public virtual override {
_safeBatchTransfer(_by(), from, to, ids, amounts, data);
}

/// @notice Checks if `operator` is approved to manage all of `owner`'s tokens.
/// @param owner The address owning the tokens.
/// @param operator The address to query for approval.
/// @return True if `operator` is approved, otherwise false.
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
if (operator == _CONDUIT) return true;
return ERC1155.isApprovedForAll(owner, operator);
}

/// @dev Determines the address initiating the transfer.
/// If the caller is the predefined conduit, returns address(0), else returns the caller's address.
/// @return result The address initiating the transfer.
function _by() internal view virtual returns (address result) {
assembly {
// `msg.sender == _CONDUIT ? address(0) : msg.sender`.
result := mul(iszero(eq(caller(), _CONDUIT)), caller())
}
}
}
492 changes: 492 additions & 0 deletions contracts/nft/erc1155m/clones/ERC1155MagicDropCloneable.sol

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,94 +1,95 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol";

import {ERC2981} from "solady/src/tokens/ERC2981.sol";
import {Ownable} from "solady/src/auth/Ownable.sol";
import {Initializable} from "solady/src/utils/Initializable.sol";

import {IERC721A} from "erc721a/contracts/IERC721A.sol";

import {ERC721AConduitPreapprovedCloneable} from "./ERC721AConduitPreapprovedCloneable.sol";
import {ERC721ACloneable} from "./ERC721ACloneable.sol";
import {ERC721AQueryableCloneable} from "./ERC721AQueryableCloneable.sol";
import {IERC721MagicDropMetadata} from "../interfaces/IERC721MagicDropMetadata.sol";
import {ERC1155} from "solady/src/tokens/ERC1155.sol";
import {IERC1155MagicDropMetadata} from "../interfaces/IERC1155MagicDropMetadata.sol";
import {ERC1155ConduitPreapprovedCloneable} from "./ERC1155ConduitPreapprovedCloneable.sol";

/// @title ERC721MagicDropMetadataCloneable
/// @notice A cloneable ERC-721A implementation that supports adjustable metadata URIs, royalty configuration.
/// @title ERC1155MagicDropMetadataCloneable
/// @notice A cloneable ERC-1155 implementation that supports adjustable metadata URIs, royalty configuration.
/// Inherits conduit-based preapprovals, making distribution more gas-efficient.
contract ERC721MagicDropMetadataCloneable is
ERC721AConduitPreapprovedCloneable,
IERC721MagicDropMetadata,
contract ERC1155MagicDropMetadataCloneable is
ERC1155ConduitPreapprovedCloneable,
IERC1155MagicDropMetadata,
ERC2981,
Ownable
Ownable,
Initializable
{
/*==============================================================
= INITIALIZERS =
==============================================================*/
/// @dev The name of the collection.
string internal _name;

/// @notice Initializes the contract.
/// @dev This function is called by the initializer of the parent contract.
/// @param owner The address of the contract owner.
function __ERC721MagicDropMetadataCloneable__init(address owner) internal onlyInitializing {
_initializeOwner(owner);
/// @dev The symbol of the collection.
string internal _symbol;

emit MagicDropTokenDeployed();
}
/// @dev The contract URI.
string internal _contractURI;

/*==============================================================
= STORAGE =
==============================================================*/
/// @dev The base URI for the collection.
string internal _baseURI;

/// @notice The base URI used to construct `tokenURI` results.
/// @dev This value can be updated by the contract owner. Typically points to an off-chain IPFS/HTTPS endpoint.
string internal _tokenBaseURI;
/// @dev The address that receives royalty payments.
address internal _royaltyReceiver;

/// @notice A URI providing contract-level metadata (e.g., for marketplaces).
/// @dev Can be updated by the owner. Often returns metadata in a JSON format describing the project.
string internal _contractURI;
/// @dev The royalty basis points.
uint96 internal _royaltyBps;

/// @notice The maximum total number of tokens that can ever be minted.
/// @dev Acts as a cap on supply. Decreasing is allowed (if no tokens are over that limit),
/// but increasing supply is forbidden after initialization.
uint256 internal _maxSupply;
/// @dev The total supply of each token.
mapping(uint256 => TokenSupply) internal _tokenSupply;

/// @notice The per-wallet minting limit, restricting how many tokens a single address can mint.
uint256 internal _walletLimit;
/// @dev The maximum number of tokens that can be minted by a single wallet.
mapping(uint256 => uint256) internal _walletLimit;

/// @notice The address receiving royalty payments.
address internal _royaltyReceiver;
/// @dev The total number of tokens minted by each user per token.
mapping(address => mapping(uint256 => uint256)) internal _totalMintedByUserPerToken;

/// @notice The royalty amount (in basis points) for secondary sales (e.g., 100 = 1%).
uint96 internal _royaltyBps;
/*==============================================================
= INITIALIZERS =
==============================================================*/

/// @notice Initializes the contract with a name, symbol, and owner.
/// @dev Can only be called once. It sets the owner, emits a deploy event, and prepares the token for minting stages.
/// @param name_ The ERC-1155 name of the collection.
/// @param symbol_ The ERC-1155 symbol of the collection.
/// @param owner_ The address designated as the initial owner of the contract.
function __ERC1155MagicDropMetadataCloneable__init(string memory name_, string memory symbol_, address owner_)
internal
onlyInitializing
{
_name = name_;
_symbol = symbol_;
_initializeOwner(owner_);

emit MagicDropTokenDeployed();
}

/*==============================================================
= PUBLIC VIEW METHODS =
==============================================================*/

/// @notice Returns the name of the collection.
function name() public view returns (string memory) {
return _name;
}

/// @notice Returns the symbol of the collection.
function symbol() public view returns (string memory) {
return _symbol;
}

/// @notice Returns the current base URI used to construct token URIs.
/// @return The base URI as a string.
function baseURI() public view override returns (string memory) {
return _tokenBaseURI;
return _baseURI;
}

/// @notice Returns a URI representing contract-level metadata, often used by marketplaces.
/// @return The contract-level metadata URI.
function contractURI() public view override returns (string memory) {
return _contractURI;
}

/// @notice The maximum number of tokens that can ever be minted by this contract.
/// @return The maximum supply of tokens.
function maxSupply() public view returns (uint256) {
return _maxSupply;
}

/// @notice The maximum number of tokens any single wallet can mint.
/// @return The minting limit per wallet.
function walletLimit() public view returns (uint256) {
return _walletLimit;
}

/// @notice The address designated to receive royalty payments on secondary sales.
/// @return The royalty receiver address.
function royaltyAddress() public view returns (address) {
Expand All @@ -101,20 +102,49 @@ contract ERC721MagicDropMetadataCloneable is
return _royaltyBps;
}

/// @notice The maximum number of tokens that can ever be minted by this contract.
/// @param tokenId The ID of the token.
/// @return The maximum supply of tokens.
function maxSupply(uint256 tokenId) public view returns (uint256) {
return _tokenSupply[tokenId].maxSupply;
}

/// @notice Return the total supply of a token.
/// @param tokenId The ID of the token.
/// @return The total supply of token.
function totalSupply(uint256 tokenId) public view returns (uint256) {
return _tokenSupply[tokenId].totalSupply;
}

/// @notice Return the total number of tokens minted for a specific token.
/// @param tokenId The ID of the token.
/// @return The total number of tokens minted.
function totalMinted(uint256 tokenId) public view returns (uint256) {
return _tokenSupply[tokenId].totalMinted;
}

/// @notice Return the maximum number of tokens any single wallet can mint for a specific token.
/// @param tokenId The ID of the token.
/// @return The minting limit per wallet.
function walletLimit(uint256 tokenId) public view returns (uint256) {
return _walletLimit[tokenId];
}

/// @notice Indicates whether this contract implements a given interface.
/// @dev Supports ERC-2981 (royalties) and ERC-4906 (batch metadata updates), in addition to inherited interfaces.
/// @param interfaceId The interface ID to check for compliance.
/// @return True if the contract implements the specified interface, otherwise false.
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC2981, ERC721ACloneable, IERC721A)
returns (bool)
{
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155, ERC2981) returns (bool) {
return interfaceId == 0x2a55205a // ERC-2981 royalties
|| interfaceId == 0x49064906 // ERC-4906 metadata updates
|| ERC721ACloneable.supportsInterface(interfaceId);
|| interfaceId == type(IERC1155MagicDropMetadata).interfaceId || ERC1155.supportsInterface(interfaceId);
}

/// @notice Returns the URI for a given token ID.
/// @dev This returns the base URI for all tokens.
/// @return The URI for the token.
function uri(uint256 /* tokenId */ ) public view override returns (string memory) {
return _baseURI;
}

/*==============================================================
Expand All @@ -137,16 +167,18 @@ contract ERC721MagicDropMetadataCloneable is

/// @notice Adjusts the maximum token supply.
/// @dev Cannot increase beyond the original max supply. Cannot set below the current minted amount.
/// @param tokenId The ID of the token to update.
/// @param newMaxSupply The new maximum supply.
function setMaxSupply(uint256 newMaxSupply) external onlyOwner {
_setMaxSupply(newMaxSupply);
function setMaxSupply(uint256 tokenId, uint256 newMaxSupply) external onlyOwner {
_setMaxSupply(tokenId, newMaxSupply);
}

/// @notice Updates the per-wallet minting limit.
/// @dev This can be changed at any time to adjust distribution constraints.
/// @param tokenId The ID of the token.
/// @param newWalletLimit The new per-wallet limit on minted tokens.
function setWalletLimit(uint256 newWalletLimit) external onlyOwner {
_setWalletLimit(newWalletLimit);
function setWalletLimit(uint256 tokenId, uint256 newWalletLimit) external onlyOwner {
_setWalletLimit(tokenId, newWalletLimit);
}

/// @notice Configures the royalty information for secondary sales.
Expand All @@ -169,64 +201,61 @@ contract ERC721MagicDropMetadataCloneable is
= INTERNAL HELPERS =
==============================================================*/

/// @notice Internal function returning the current base URI for token metadata.
/// @return The current base URI string.
function _baseURI() internal view override returns (string memory) {
return _tokenBaseURI;
}

/// @notice Internal function setting the base URI for token metadata.
/// @param newBaseURI The new base URI string.
function _setBaseURI(string calldata newBaseURI) internal {
_tokenBaseURI = newBaseURI;
_baseURI = newBaseURI;

if (totalSupply() != 0) {
// Notify EIP-4906 compliant observers of a metadata update.
emit BatchMetadataUpdate(0, totalSupply() - 1);
}
// Notify EIP-4906 compliant observers of a metadata update.
emit BatchMetadataUpdate(0, type(uint256).max);
}

/// @notice Internal function setting the contract URI.
/// @param newContractURI The new contract metadata URI.
function _setContractURI(string calldata newContractURI) internal {
_contractURI = newContractURI;

emit ContractURIUpdated(newContractURI);
}

/// @notice Internal function setting the royalty information.
/// @param newReceiver The address to receive royalties.
/// @param newBps The royalty rate in basis points (e.g., 100 = 1%).
function _setRoyaltyInfo(address newReceiver, uint96 newBps) internal {
_royaltyReceiver = newReceiver;
_royaltyBps = newBps;
super._setDefaultRoyalty(newReceiver, newBps);
emit RoyaltyInfoUpdated(newReceiver, newBps);
}

/// @notice Internal function setting the maximum token supply.
/// @dev Cannot increase beyond the original max supply. Cannot set below the current minted amount.
/// @param tokenId The ID of the token.
/// @param newMaxSupply The new maximum supply.
function _setMaxSupply(uint256 newMaxSupply) internal {
if (_maxSupply != 0 && newMaxSupply > _maxSupply) {
function _setMaxSupply(uint256 tokenId, uint256 newMaxSupply) internal {
uint256 oldMaxSupply = _tokenSupply[tokenId].maxSupply;
if (oldMaxSupply != 0 && newMaxSupply > oldMaxSupply) {
revert MaxSupplyCannotBeIncreased();
}

if (newMaxSupply < _totalMinted()) {
if (newMaxSupply < _tokenSupply[tokenId].totalMinted) {
revert MaxSupplyCannotBeLessThanCurrentSupply();
}

if (newMaxSupply > 2 ** 64 - 1) {
revert MaxSupplyCannotBeGreaterThan2ToThe64thPower();
}

_maxSupply = newMaxSupply;
emit MaxSupplyUpdated(newMaxSupply);
_tokenSupply[tokenId].maxSupply = uint64(newMaxSupply);

emit MaxSupplyUpdated(tokenId, oldMaxSupply, newMaxSupply);
}

/// @notice Internal function setting the per-wallet minting limit.
/// @param tokenId The ID of the token.
/// @param newWalletLimit The new per-wallet limit on minted tokens.
function _setWalletLimit(uint256 newWalletLimit) internal {
_walletLimit = newWalletLimit;
emit WalletLimitUpdated(newWalletLimit);
}

/// @notice Internal function setting the royalty information.
/// @param newReceiver The address to receive royalties.
/// @param newBps The royalty rate in basis points (e.g., 100 = 1%).
function _setRoyaltyInfo(address newReceiver, uint96 newBps) internal {
_royaltyReceiver = newReceiver;
_royaltyBps = newBps;
super._setDefaultRoyalty(_royaltyReceiver, _royaltyBps);
emit RoyaltyInfoUpdated(_royaltyReceiver, _royaltyBps);
}

/// @notice Internal function setting the contract URI.
/// @param newContractURI The new contract metadata URI.
function _setContractURI(string calldata newContractURI) internal {
_contractURI = newContractURI;
emit ContractURIUpdated(newContractURI);
function _setWalletLimit(uint256 tokenId, uint256 newWalletLimit) internal {
_walletLimit[tokenId] = newWalletLimit;
emit WalletLimitUpdated(tokenId, newWalletLimit);
}
}
Loading