Contract Name:
CreatorRedeemMinterFactory
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol";
import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { IContractMetadata } from "../../interfaces/IContractMetadata.sol";
import { IVersionedContract } from "../../interfaces/IVersionedContract.sol";
import { IMinter1155 } from "../../interfaces/IMinter1155.sol";
import { ICreatorCommands } from "../../interfaces/ICreatorCommands.sol";
import { CreatorRedeemMinterStrategy } from "./CreatorRedeemMinterStrategy.sol";
import { ICreator1155 } from "../../interfaces/ICreator1155.sol";
import { SharedBaseConstants } from "../../shared/SharedBaseConstants.sol";
/// @title CreatorRedeemMinterFactory
/// @notice A factory for CreatorRedeemMinterStrategy contracts
contract CreatorRedeemMinterFactory is IContractMetadata, SharedBaseConstants, IVersionedContract, IMinter1155 {
bytes4 constant LEGACY_IMINTER1155_INTERFACE_ID = 0x6467a6fc;
address public immutable redeemMinterImplementation;
event RedeemMinterDeployed(address indexed creatorContract, address indexed minterContract);
error CallerNotCreator1155();
error MinterContractAlreadyExists();
error MinterContractDoesNotExist();
constructor() {
redeemMinterImplementation = address(new CreatorRedeemMinterStrategy());
}
/// @notice Factory contract name
function contractName() external pure override returns (string memory) {
return "Redeem Minter Factory";
}
/// @notice Factory contract version
function contractVersion() external pure override returns (string memory) {
return "1";
}
/// @notice No-op function for IMinter1155 compatibility
function requestMint(
address sender,
uint256 tokenId,
uint256 quantity,
uint256 ethValueSent,
bytes calldata minterArguments
)
external
returns (ICreatorCommands.CommandSet memory commands)
{ }
/// @notice IERC165 interface support
function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
return interfaceId == type(IMinter1155).interfaceId || interfaceId == type(IERC165).interfaceId;
}
/// @notice Deploys a new CreatorRedeemMinterStrategy for caller Creator1155 contract if none exists
function createMinterIfNoneExists() external {
if (!IERC165(msg.sender).supportsInterface(type(ICreator1155).interfaceId)) {
revert CallerNotCreator1155();
}
if (doesRedeemMinterExistForCreatorContract(msg.sender)) {
return;
}
address minter = Clones.cloneDeterministic(redeemMinterImplementation, keccak256(abi.encode(msg.sender)));
CreatorRedeemMinterStrategy(minter).initialize(msg.sender);
emit RedeemMinterDeployed(msg.sender, minter);
}
/// @notice Returns deterministic address of a CreatorRedeemMinterStrategy for a given Creator1155 contract
/// @param _creatorContract Creator1155 contract address
/// @return Address of CreatorRedeemMinterStrategy
function predictMinterAddress(address _creatorContract) public view returns (address) {
return Clones.predictDeterministicAddress(
redeemMinterImplementation, keccak256(abi.encode(_creatorContract)), address(this)
);
}
/// @notice Returns true if a CreatorRedeemMinterStrategy has been deployed for a given Creator1155 contract
/// @param _creatorContract Creator1155 contract address
/// @return True if a CreatorRedeemMinterStrategy has been deployed for a given Creator1155 contract
function doesRedeemMinterExistForCreatorContract(address _creatorContract) public view returns (bool) {
return predictMinterAddress(_creatorContract).code.length > 0;
}
/// @notice Returns address of deployed CreatorRedeemMinterStrategy for a given Creator1155 contract
/// @param _creatorContract Creator1155 contract address
/// @return Address of deployed CreatorRedeemMinterStrategy
function getDeployedRedeemMinterForCreatorContract(address _creatorContract) external view returns (address) {
address minter = predictMinterAddress(_creatorContract);
if (minter.code.length == 0) {
revert MinterContractDoesNotExist();
}
return minter;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol)
pragma solidity ^0.8.0;
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*
* _Available since v3.4._
*/
library Clones {
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(0, 0x09, 0x37)
}
require(instance != address(0), "ERC1167: create failed");
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(0, 0x09, 0x37, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := keccak256(add(ptr, 0x43), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IContractMetadata {
/// @notice Contract name returns the pretty contract name
function contractName() external returns (string memory);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IVersionedContract {
function contractVersion() external returns (string memory);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC165Upgradeable.sol";
import { ICreatorCommands } from "./ICreatorCommands.sol";
/// @notice Minter standard interface
/// @dev Minters need to confirm to the ERC165 selector of type(IMinter1155).interfaceId
interface IMinter1155 is IERC165Upgradeable {
function requestMint(
address sender,
uint256 tokenId,
uint256 quantity,
uint256 ethValueSent,
bytes calldata minterArguments
)
external
returns (ICreatorCommands.CommandSet memory commands);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/// @notice Creator Commands used by minter modules passed back to the main modules
interface ICreatorCommands {
/// @notice This enum is used to define supported creator action types.
/// This can change in the future
enum CreatorActions
// No operation - also the default for mintings that may not return a command
{
NO_OP,
// Send ether
SEND_ETH,
// Mint operation
MINT
}
/// @notice This command is for
struct Command {
// Method for operation
CreatorActions method;
// Arguments used for this operation
bytes args;
}
/// @notice This command set is returned from the minter back to the user
struct CommandSet {
Command[] commands;
uint256 at;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ICreatorCommands } from "../../interfaces/ICreatorCommands.sol";
import { SaleStrategy } from "../SaleStrategy.sol";
import { SaleCommandHelper } from "../utils/SaleCommandHelper.sol";
/// @title CreatorRedeemMinterStrategy
/// @notice A strategy that allows minting by redeeming other (ERC20/721/1155) tokens
contract CreatorRedeemMinterStrategy is SaleStrategy, Initializable {
using SaleCommandHelper for ICreatorCommands.CommandSet;
using SafeERC20 for IERC20;
enum TokenType {
NULL,
ERC721,
ERC1155,
ERC20
}
struct MintToken {
/// @notice The address of the minting token contract (always creatorContract)
address tokenContract;
/// @notice The mint tokenId
uint256 tokenId;
/// @notice The amount of tokens that can be minted
uint256 amount;
/// @notice The mint token type (alwas ERC1155)
TokenType tokenType;
}
struct RedeemInstruction {
/// @notice The type of token to be redeemed
TokenType tokenType;
/// @notice The amount of tokens to be redeemed
uint256 amount;
/// @notice The start of the range of token ids to be redeemed
uint256 tokenIdStart;
/// @notice The end of the range of token ids to be redeemed
uint256 tokenIdEnd;
/// @notice The address of the token contract to be redeemed
address tokenContract;
/// @notice The address to transfer the redeemed tokens to
address transferRecipient;
/// @notice The function to call on the token contract to burn the tokens
bytes4 burnFunction;
}
struct RedeemInstructions {
/// @notice The token to be minted
MintToken mintToken;
/// @notice The instructions for redeeming tokens
RedeemInstruction[] instructions;
/// @notice The start of the sale
uint64 saleStart;
/// @notice The end of the sale
uint64 saleEnd;
/// @notice The amount of ETH to send to the recipient
uint256 ethAmount;
/// @notice The address to send the ETH to (0x0 for the creator contract)
address ethRecipient;
}
event RedeemSet(address indexed target, bytes32 indexed redeemsInstructionsHash, RedeemInstructions data);
event RedeemProcessed(
address indexed target,
bytes32 indexed redeemsInstructionsHash,
address sender,
uint256[][] tokenIds,
uint256[][] amounts
);
event RedeemsCleared(address indexed target, bytes32[] indexed redeemInstructionsHashes);
error RedeemInstructionAlreadySet();
error RedeemInstructionNotAllowed();
error IncorrectNumberOfTokenIds();
error InvalidTokenIdsForTokenType();
error InvalidSaleEndOrStart();
error EmptyRedeemInstructions();
error MintTokenTypeMustBeERC1155();
error MustBurnOrTransfer();
error IncorrectMintAmount();
error IncorrectBurnOrTransferAmount();
error InvalidCreatorContract();
error SaleEnded();
error SaleHasNotStarted();
error InvalidTokenType();
error WrongValueSent();
error CallerNotCreatorContract();
error BurnFailed();
error MustCallClearRedeem();
error TokenIdOutOfRange();
error MintTokenContractMustBeCreatorContract();
error SenderIsNotTokenOwner();
/// @notice tokenId, keccak256(abi.encode(RedeemInstructions)) => redeem instructions are allowed
mapping(uint256 => mapping(bytes32 => bool)) public redeemInstructionsHashIsAllowed;
/// @notice creator contract
address public creatorContract;
modifier onlyCreatorContract() {
if (msg.sender != creatorContract) {
revert CallerNotCreatorContract();
}
_;
}
function initialize(address _creatorContract) public initializer {
if (_creatorContract == address(0)) {
revert InvalidCreatorContract();
}
creatorContract = _creatorContract;
}
/// @notice Redeem Minter Strategy contract name
function contractName() external pure override returns (string memory) {
return "Redeem Minter Sale Strategy";
}
/// @notice Redeem Minter Strategy contract version
function contractVersion() external pure override returns (string memory) {
return "1";
}
/// @notice Redeem instructions object hash
/// @param _redeemInstructions The redeem instructions object
function redeemInstructionsHash(RedeemInstructions memory _redeemInstructions) public pure returns (bytes32) {
return keccak256(abi.encode(_redeemInstructions));
}
function validateSingleRedeemInstruction(RedeemInstruction calldata _redeemInstruction) internal pure {
if (_redeemInstruction.tokenType == TokenType.ERC20) {
if (_redeemInstruction.tokenIdStart != 0 || _redeemInstruction.tokenIdEnd != 0) {
revert InvalidTokenIdsForTokenType();
}
} else if (
_redeemInstruction.tokenType == TokenType.ERC721 || _redeemInstruction.tokenType == TokenType.ERC1155
) {
if (_redeemInstruction.tokenIdStart > _redeemInstruction.tokenIdEnd) {
revert InvalidTokenIdsForTokenType();
}
} else {
revert InvalidTokenType();
}
if (_redeemInstruction.burnFunction != 0 && _redeemInstruction.transferRecipient != address(0)) {
revert MustBurnOrTransfer();
}
if (_redeemInstruction.burnFunction == 0 && _redeemInstruction.transferRecipient == address(0)) {
revert MustBurnOrTransfer();
}
if (_redeemInstruction.amount == 0) {
revert IncorrectMintAmount();
}
}
/// @notice Validate redeem instructions
/// @param _redeemInstructions The redeem instructions object
function validateRedeemInstructions(RedeemInstructions calldata _redeemInstructions) public view {
if (
_redeemInstructions.saleEnd <= _redeemInstructions.saleStart
|| _redeemInstructions.saleEnd <= block.timestamp
) {
revert InvalidSaleEndOrStart();
}
if (_redeemInstructions.instructions.length == 0) {
revert EmptyRedeemInstructions();
}
if (_redeemInstructions.mintToken.tokenContract != creatorContract) {
revert MintTokenContractMustBeCreatorContract();
}
if (_redeemInstructions.mintToken.tokenType != TokenType.ERC1155) {
revert MintTokenTypeMustBeERC1155();
}
uint256 numInstructions = _redeemInstructions.instructions.length;
unchecked {
for (uint256 i; i < numInstructions; ++i) {
validateSingleRedeemInstruction(_redeemInstructions.instructions[i]);
}
}
}
/// @notice Set redeem instructions
/// @param tokenId The token id to set redeem instructions for
/// @param _redeemInstructions The redeem instructions object
function setRedeem(uint256 tokenId, RedeemInstructions calldata _redeemInstructions) external onlyCreatorContract {
if (_redeemInstructions.mintToken.tokenId != tokenId) {
revert InvalidTokenIdsForTokenType();
}
validateRedeemInstructions(_redeemInstructions);
bytes32 hash = redeemInstructionsHash(_redeemInstructions);
if (redeemInstructionsHashIsAllowed[tokenId][hash]) {
revert RedeemInstructionAlreadySet();
}
redeemInstructionsHashIsAllowed[tokenId][hash] = true;
emit RedeemSet(creatorContract, hash, _redeemInstructions);
}
/// @notice Clear redeem instructions
/// @param tokenId The token id to clear redeem instructions for
/// @param hashes Array of redeem instructions hashes to clear
function clearRedeem(uint256 tokenId, bytes32[] calldata hashes) external onlyCreatorContract {
uint256 numHashes = hashes.length;
unchecked {
for (uint256 i; i < numHashes; ++i) {
redeemInstructionsHashIsAllowed[tokenId][hashes[i]] = false;
}
}
emit RedeemsCleared(creatorContract, hashes);
}
/// @notice Request mint
/// @param tokenId The token id to mint
/// @param amount The amount to mint
/// @param ethValueSent The amount of eth sent
/// @param minterArguments The abi encoded minter arguments (address, RedeemInstructions, uint256[][], uint256[][])
function requestMint(
address sender,
uint256 tokenId,
uint256 amount,
uint256 ethValueSent,
bytes calldata minterArguments
)
external
onlyCreatorContract
returns (ICreatorCommands.CommandSet memory commands)
{
(RedeemInstructions memory redeemInstructions, uint256[][] memory tokenIds, uint256[][] memory amounts) = abi.decode(
minterArguments,
(RedeemInstructions, uint256[][], uint256[][])
);
bytes32 hash = redeemInstructionsHash(redeemInstructions);
if (tokenId != redeemInstructions.mintToken.tokenId) {
revert InvalidTokenIdsForTokenType();
}
if (!redeemInstructionsHashIsAllowed[tokenId][hash]) {
revert RedeemInstructionNotAllowed();
}
if (redeemInstructions.saleStart > block.timestamp) {
revert SaleHasNotStarted();
}
if (redeemInstructions.saleEnd < block.timestamp) {
revert SaleEnded();
}
if (redeemInstructions.instructions.length != tokenIds.length) {
revert IncorrectNumberOfTokenIds();
}
if (ethValueSent != redeemInstructions.ethAmount) {
revert WrongValueSent();
}
if (amount != redeemInstructions.mintToken.amount) {
revert IncorrectMintAmount();
}
uint256 numInstructions = redeemInstructions.instructions.length;
unchecked {
for (uint256 i; i < numInstructions; ++i) {
RedeemInstruction memory instruction = redeemInstructions.instructions[i];
if (instruction.tokenType == TokenType.ERC1155) {
_handleErc1155Redeem(sender, instruction, tokenIds[i], amounts[i]);
} else if (instruction.tokenType == TokenType.ERC721) {
_handleErc721Redeem(sender, instruction, tokenIds[i]);
} else if (instruction.tokenType == TokenType.ERC20) {
_handleErc20Redeem(sender, instruction);
}
}
}
bool shouldTransferFunds = redeemInstructions.ethRecipient != address(0);
commands.setSize(shouldTransferFunds ? 2 : 1);
commands.mint(sender, tokenId, amount);
if (shouldTransferFunds) {
commands.transfer(redeemInstructions.ethRecipient, ethValueSent);
}
emit RedeemProcessed(creatorContract, hash, sender, tokenIds, amounts);
}
function _handleErc721Redeem(
address sender,
RedeemInstruction memory instruction,
uint256[] memory tokenIds
)
internal
{
uint256 numTokenIds = tokenIds.length;
if (numTokenIds != instruction.amount) {
revert IncorrectBurnOrTransferAmount();
}
unchecked {
for (uint256 j; j < numTokenIds; j++) {
if (tokenIds[j] < instruction.tokenIdStart || tokenIds[j] > instruction.tokenIdEnd) {
revert TokenIdOutOfRange();
}
if (instruction.burnFunction != 0) {
if (IERC721(instruction.tokenContract).ownerOf(tokenIds[j]) != sender) {
revert SenderIsNotTokenOwner();
}
(bool success,) =
instruction.tokenContract.call(abi.encodeWithSelector(instruction.burnFunction, tokenIds[j]));
if (!success) {
revert BurnFailed();
}
} else {
IERC721(instruction.tokenContract).safeTransferFrom(
sender, instruction.transferRecipient, tokenIds[j]
);
}
}
}
}
function _handleErc1155Redeem(
address sender,
RedeemInstruction memory instruction,
uint256[] memory tokenIds,
uint256[] memory amounts
)
internal
{
uint256 numTokenIds = tokenIds.length;
if (amounts.length != numTokenIds) {
revert IncorrectNumberOfTokenIds();
}
uint256 sum;
for (uint256 j = 0; j < numTokenIds;) {
sum += amounts[j];
if (tokenIds[j] < instruction.tokenIdStart || tokenIds[j] > instruction.tokenIdEnd) {
revert TokenIdOutOfRange();
}
unchecked {
++j;
}
}
if (sum != instruction.amount) {
revert IncorrectBurnOrTransferAmount();
}
if (instruction.burnFunction != 0) {
(bool success,) = instruction.tokenContract.call(
abi.encodeWithSelector(instruction.burnFunction, sender, tokenIds, amounts)
);
if (!success) {
revert BurnFailed();
}
} else {
IERC1155(instruction.tokenContract).safeBatchTransferFrom(
sender, instruction.transferRecipient, tokenIds, amounts, ""
);
}
}
function _handleErc20Redeem(address sender, RedeemInstruction memory instruction) internal {
if (instruction.burnFunction != 0) {
(bool success,) = instruction.tokenContract.call(
abi.encodeWithSelector(instruction.burnFunction, sender, instruction.amount)
);
if (!success) {
revert BurnFailed();
}
} else {
IERC20(instruction.tokenContract).transferFrom(sender, instruction.transferRecipient, instruction.amount);
}
}
/// @notice Reset sale - Use clearRedeem instead
function resetSale(uint256) external view override onlyCreatorContract {
revert MustCallClearRedeem();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC165Upgradeable.sol";
import { IERC1155MetadataURIUpgradeable } from
"@openzeppelin/contracts-upgradeable/interfaces/IERC1155MetadataURIUpgradeable.sol";
import { ICreator1155TypesV1 } from "../nft/ICreator1155TypesV1.sol";
import { IRenderer1155 } from "../interfaces/IRenderer1155.sol";
import { IMinter1155 } from "../interfaces/IMinter1155.sol";
import { IOwnable } from "../interfaces/IOwnable.sol";
import { IVersionedContract } from "./IVersionedContract.sol";
import { ICreatorRoyaltiesControl } from "../interfaces/ICreatorRoyaltiesControl.sol";
/// @notice Main interface for the Creator1155 contract
interface ICreator1155 is ICreator1155TypesV1, IVersionedContract, IOwnable, IERC1155MetadataURIUpgradeable {
function PERMISSION_BIT_ADMIN() external returns (uint256);
function PERMISSION_BIT_MINTER() external returns (uint256);
function PERMISSION_BIT_SALES() external returns (uint256);
function PERMISSION_BIT_METADATA() external returns (uint256);
/// @notice Used to label the configuration update type
enum ConfigUpdate {
OWNER,
FUNDS_RECIPIENT,
TRANSFER_HOOK
}
event ConfigUpdated(address indexed updater, ConfigUpdate indexed updateType, ContractConfig newConfig);
event UpdatedToken(address indexed from, uint256 indexed tokenId, TokenData tokenData);
event SetupNewToken(uint256 indexed tokenId, address indexed sender, string newURI, uint256 maxSupply);
event SetupSoulbound(uint256 indexed tokenId, bool isSoulbound);
function setOwner(address newOwner) external;
event ContractRendererUpdated(IRenderer1155 renderer);
event ContractMetadataUpdated(address indexed updater, string uri, string name);
event Purchased(
address indexed sender, address indexed minter, uint256 indexed tokenId, uint256 quantity, uint256 value
);
event AdminMinted(address indexed sender, address indexed recipient, uint256 indexed tokenId, uint256 quantity);
event AdminMintedBatch(address indexed sender, address indexed minter, uint256[] tokenIds, uint256[] quantities);
error TokenIdMismatch(uint256 expected, uint256 actual);
error UserMissingRoleForToken(address user, uint256 tokenId, uint256 role);
error Config_TransferHookNotSupported(address proposedAddress);
error Mint_InsolventSaleTransfer();
error Mint_ValueTransferFail();
error Mint_TokenIDMintNotAllowed();
error Mint_UnknownCommand();
error Burn_NotOwnerOrApproved(address operator, address user);
error NewOwnerNeedsToBeAdmin();
error Sale_CannotCallNonSalesContract(address targetContract);
error CallFailed(bytes reason);
error Renderer_NotValidRendererContract();
error ETHWithdrawFailed(address recipient, uint256 amount);
error FundsWithdrawInsolvent(uint256 amount, uint256 contractValue);
error CannotMintMoreTokens(uint256 tokenId, uint256 quantity, uint256 totalMinted, uint256 maxSupply);
error Call_TokenIdMismatch();
error Transfer_NotAllowed();
/// @notice Only allow minting one token id at time
/// @dev Mint contract function that calls the underlying sales function for commands
/// @param minter Address for the minter
/// @param tokenId tokenId to mint, set to 0 for new tokenId
/// @param quantity to mint
/// @param minterArguments calldata for the minter contracts
function mint(
IMinter1155 minter,
uint256 tokenId,
uint256 quantity,
bytes calldata minterArguments
)
external
payable;
function adminMint(address recipient, uint256 tokenId, uint256 quantity, bytes memory data) external;
function adminMintBatch(
address recipient,
uint256[] memory tokenIds,
uint256[] memory quantities,
bytes memory data
)
external;
function burnBatch(address user, uint256[] calldata tokenIds, uint256[] calldata amounts) external;
/// @notice Contract call to setupNewToken
/// @param tokenURI URI for the token
/// @param maxSupply maxSupply for the token, set to 0 for open edition
/// @param isSoulbound Whether the token is soulbound
function setupNewToken(string memory tokenURI, uint256 maxSupply, bool isSoulbound) external returns (uint256 tokenId);
function updateTokenURI(uint256 tokenId, string memory _newURI) external;
function updateContractMetadata(string memory _newURI, string memory _newName) external;
// Public interface for `setTokenMetadataRenderer(uint256, address) has been deprecated.
function contractURI() external view returns (string memory);
function assumeLastTokenIdMatches(uint256 tokenId) external;
function updateRoyaltiesForToken(
uint256 tokenId,
ICreatorRoyaltiesControl.RoyaltyConfiguration memory royaltyConfiguration
)
external;
function addPermission(uint256 tokenId, address user, uint256 permissionBits) external;
function removePermission(uint256 tokenId, address user, uint256 permissionBits) external;
function isAdminOrRole(address user, uint256 tokenId, uint256 role) external view returns (bool);
function getTokenInfo(uint256 tokenId) external view returns (TokenData memory);
function callRenderer(uint256 tokenId, bytes memory data) external;
function callSale(uint256 tokenId, IMinter1155 salesConfig, bytes memory data) external;
function mintFee() external view returns (uint256);
/// @notice Withdraws all ETH from the contract to the funds recipient address
function withdraw() external;
/// @notice Returns the current implementation address
function implementation() external view returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract SharedBaseConstants {
uint256 public constant CONTRACT_BASE_ID = 0;
}
// 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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165Upgradeable.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/Address.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*
* _Available since v3.1._
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @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`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @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 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) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* 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;
/**
* @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;
/**
* @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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC165Upgradeable.sol";
import { IMinter1155 } from "../interfaces/IMinter1155.sol";
import { IContractMetadata } from "../interfaces/IContractMetadata.sol";
import { IVersionedContract } from "../interfaces/IVersionedContract.sol";
/// @notice Sales Strategy Helper contract template on top of IMinter1155
abstract contract SaleStrategy is IMinter1155, IVersionedContract, IContractMetadata {
/// @notice This function resets the sales configuration for a given tokenId and contract.
/// @dev This function is intentioned to be called directly from the affected sales contract
function resetSale(uint256 tokenId) external virtual;
function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
return interfaceId == type(IMinter1155).interfaceId || interfaceId == type(IERC165Upgradeable).interfaceId;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { ICreatorCommands } from "../../interfaces/ICreatorCommands.sol";
/// @title SaleCommandHelper
/// @notice Helper library for creating commands for the sale contract
library SaleCommandHelper {
/// @notice Sets the size of commands and initializes command array. Empty entries are skipped by the resolver.
/// @dev Beware: this removes all previous command entries from memory
/// @param commandSet command set struct storage.
/// @param size size to set for the new struct
function setSize(ICreatorCommands.CommandSet memory commandSet, uint256 size) internal pure {
commandSet.commands = new ICreatorCommands.Command[](size);
}
/// @notice Creates a command to mint a token
/// @param commandSet The command set to add the command to
/// @param to The address to mint to
/// @param tokenId The token ID to mint
/// @param quantity The quantity of tokens to mint
function mint(
ICreatorCommands.CommandSet memory commandSet,
address to,
uint256 tokenId,
uint256 quantity
)
internal
pure
{
unchecked {
commandSet.commands[commandSet.at++] = ICreatorCommands.Command({
method: ICreatorCommands.CreatorActions.MINT,
args: abi.encode(to, tokenId, quantity)
});
}
}
/// @notice Creates a command to transfer ETH
/// @param commandSet The command set to add the command to
/// @param to The address to transfer to
/// @param amount The amount of ETH to transfer
function transfer(ICreatorCommands.CommandSet memory commandSet, address to, uint256 amount) internal pure {
unchecked {
commandSet.commands[commandSet.at++] = ICreatorCommands.Command({
method: ICreatorCommands.CreatorActions.SEND_ETH,
args: abi.encode(to, amount)
});
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155MetadataURI.sol)
pragma solidity ^0.8.0;
import "../token/ERC1155/extensions/IERC1155MetadataURIUpgradeable.sol";
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { ITransferHookReceiver } from "../interfaces/ITransferHookReceiver.sol";
/// @notice Interface for types used across the Creator1155 contract
interface ICreator1155TypesV1 {
/// @notice Used to store individual token data
struct TokenData {
string uri;
uint256 maxSupply;
uint256 totalMinted;
bool isSoulbound;
}
/// @notice Used to store contract-level configuration
struct ContractConfig {
address owner;
uint96 __gap1;
address payable fundsRecipient;
uint96 __gap2;
ITransferHookReceiver transferHook;
uint96 __gap3;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC165Upgradeable.sol";
/// @dev IERC165 type required
interface IRenderer1155 is IERC165Upgradeable {
/// @notice Called for assigned tokenId, or when token id is globally set to a renderer
/// @dev contract target is assumed to be msg.sender
/// @param tokenId token id to get uri for
function uri(uint256 tokenId) external view returns (string memory);
/// @notice Only called for tokenId == 0
/// @dev contract target is assumed to be msg.sender
function contractURI() external view returns (string memory);
/// @notice Sets up renderer from contract
/// @param initData data to setup renderer with
/// @dev contract target is assumed to be msg.sender
function setup(bytes memory initData) external;
// IERC165 type required – set in base helper
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IOwnable {
function owner() external returns (address);
event OwnershipTransferred(address lastOwner, address newOwner);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { IERC2981 } from "@openzeppelin/contracts/interfaces/IERC2981.sol";
interface ICreatorRoyaltiesControl is IERC2981 {
/// @notice The RoyaltyConfiguration struct is used to store the royalty configuration for a given token.
/// @param royaltyMintSchedule Every nth token will go to the royalty recipient.
/// @param royaltyBPS The royalty amount in basis points for secondary sales.
/// @param royaltyRecipient The address that will receive the royalty payments.
struct RoyaltyConfiguration {
uint32 royaltyMintSchedule;
uint32 royaltyBPS;
address royaltyRecipient;
}
/// @notice Thrown when a user tries to have 100% supply royalties
error InvalidMintSchedule();
/// @notice Event emitted when royalties are updated
event UpdatedRoyalties(uint256 indexed tokenId, address indexed user, RoyaltyConfiguration configuration);
/// @notice External data getter to get royalties for a token
/// @param tokenId tokenId to get royalties configuration for
function getRoyalties(uint256 tokenId) external view returns (RoyaltyConfiguration memory);
}
// 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 IERC165Upgradeable {
/**
* @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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
pragma solidity ^0.8.0;
import "../IERC1155Upgradeable.sol";
/**
* @dev Interface of the optional ERC1155MetadataExtension interface, as defined
* in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
*
* _Available since v3.1._
*/
interface IERC1155MetadataURIUpgradeable is IERC1155Upgradeable {
/**
* @dev Returns the URI for token type `id`.
*
* If the `\{id\}` substring is present in the URI, it must be replaced by
* clients with the actual token type ID.
*/
function uri(uint256 id) external view returns (string memory);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC165Upgradeable.sol";
interface ITransferHookReceiver is IERC165Upgradeable {
/// @notice Token transfer batch callback
/// @param target target contract for transfer
/// @param operator operator address for transfer
/// @param from user address for amount transferred
/// @param to user address for amount transferred
/// @param ids list of token ids transferred
/// @param amounts list of values transferred
/// @param data data as perscribed by 1155 standard
function onTokenTransferBatch(
address target,
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
)
external;
// IERC165 type required
}
// 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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165Upgradeable.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*
* _Available since v3.1._
*/
interface IERC1155Upgradeable is IERC165Upgradeable {
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}