From 22fe7ac64d58e1eeddb92c48217fb55febd234a5 Mon Sep 17 00:00:00 2001 From: iain nash Date: Wed, 27 Sep 2023 17:02:34 -0400 Subject: [PATCH] remove unnecessary changes --- .env.anvil | 3 +- script/DeployPreminter.s.sol | 80 ------ src/premint/README.md | 59 ----- src/premint/ZoraCreator1155Attribution.sol | 240 ------------------ .../ZoraCreator1155PremintExecutor.sol | 212 ---------------- .../ZoraCreatorRedeemMinterFactory.t.sol | 68 +++-- .../ZoraCreatorRedeemMinterStrategy.t.sol | 2 +- 7 files changed, 35 insertions(+), 629 deletions(-) delete mode 100644 script/DeployPreminter.s.sol delete mode 100644 src/premint/README.md delete mode 100644 src/premint/ZoraCreator1155Attribution.sol delete mode 100644 src/premint/ZoraCreator1155PremintExecutor.sol diff --git a/.env.anvil b/.env.anvil index 4e183d130..0c2078aa6 100644 --- a/.env.anvil +++ b/.env.anvil @@ -1,3 +1,2 @@ FORK_RPC_URL="https://testnet.rpc.zora.co/" -# FORK_BLOCK_NUMBER=916572 -FORK_BLOCK_NUMBER=700700 +FORK_BLOCK_NUMBER=916572 \ No newline at end of file diff --git a/script/DeployPreminter.s.sol b/script/DeployPreminter.s.sol deleted file mode 100644 index 4126fd9b1..000000000 --- a/script/DeployPreminter.s.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import "forge-std/Script.sol"; -import "forge-std/console2.sol"; - -import {ZoraDeployerBase} from "./ZoraDeployerBase.sol"; -import {ChainConfig, Deployment} from "../src/deployment/DeploymentConfig.sol"; - -import {ZoraCreator1155FactoryImpl} from "../src/factory/ZoraCreator1155FactoryImpl.sol"; -import {Zora1155Factory} from "../src/proxies/Zora1155Factory.sol"; -import {ZoraCreator1155Impl} from "../src/nft/ZoraCreator1155Impl.sol"; -import {ICreatorRoyaltiesControl} from "../src/interfaces/ICreatorRoyaltiesControl.sol"; -import {IZoraCreator1155Factory} from "../src/interfaces/IZoraCreator1155Factory.sol"; -import {IMinter1155} from "../src/interfaces/IMinter1155.sol"; -import {IZoraCreator1155} from "../src/interfaces/IZoraCreator1155.sol"; -import {ProxyShim} from "../src/utils/ProxyShim.sol"; -import {ZoraCreatorFixedPriceSaleStrategy} from "../src/minters/fixed-price/ZoraCreatorFixedPriceSaleStrategy.sol"; -import {ZoraCreatorMerkleMinterStrategy} from "../src/minters/merkle/ZoraCreatorMerkleMinterStrategy.sol"; -import {ZoraCreatorRedeemMinterFactory} from "../src/minters/redeem/ZoraCreatorRedeemMinterFactory.sol"; -import {ZoraCreator1155PremintExecutor} from "../src/premint/ZoraCreator1155PremintExecutor.sol"; - -contract DeployPreminter is ZoraDeployerBase { - function run() public returns (string memory) { - Deployment memory deployment = getDeployment(); - - uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); - - // bool deployFactory = vm.envBool("DEPLOY_FACTORY"); - bool deployFactory = vm.envBool("DEPLOY_FACTORY"); - - IZoraCreator1155Factory factoryProxy; - vm.startBroadcast(deployerPrivateKey); - - if (deployFactory) { - address deployer = vm.envAddress("DEPLOYER"); - address factoryShimAddress = address(new ProxyShim(deployer)); - ChainConfig memory chainConfig = getChainConfig(); - - factoryProxy = IZoraCreator1155Factory(address(new Zora1155Factory(factoryShimAddress, ""))); - - deployment.factoryProxy = address(factoryProxy); - - ZoraCreator1155Impl creatorImpl = new ZoraCreator1155Impl( - chainConfig.mintFeeAmount, - chainConfig.mintFeeRecipient, - address(factoryProxy), - chainConfig.protocolRewards - ); - - deployment.contract1155Impl = address(creatorImpl); - - ZoraCreator1155FactoryImpl factoryImpl = new ZoraCreator1155FactoryImpl({ - _implementation: creatorImpl, - _merkleMinter: IMinter1155(deployment.merkleMintSaleStrategy), - _redeemMinterFactory: IMinter1155(deployment.redeemMinterFactory), - _fixedPriceMinter: IMinter1155(deployment.fixedPriceSaleStrategy) - }); - - // Upgrade to "real" factory address - ZoraCreator1155FactoryImpl(address(factoryProxy)).upgradeTo(address(factoryImpl)); - ZoraCreator1155FactoryImpl(address(factoryProxy)).initialize(chainConfig.factoryOwner); - - deployment.factoryImpl = address(factoryImpl); - } else { - factoryProxy = ZoraCreator1155FactoryImpl(deployment.factoryProxy); - } - - console.log("!!!factory proxy!!!"); - // console.log(factoryProxy); - - ZoraCreator1155PremintExecutor preminter = new ZoraCreator1155PremintExecutor(factoryProxy); - - vm.stopBroadcast(); - - deployment.preminter = address(preminter); - - return getDeploymentJSON(deployment); - } -} diff --git a/src/premint/README.md b/src/premint/README.md deleted file mode 100644 index 6b65baff7..000000000 --- a/src/premint/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Preminter - -## Design - -A Preminter contract validates signatures and executes actions to 1. deploy contracts, 2. create tokens, 3. setup created token parameters -4. mint tokens to the executor of the transaction as a reward. - -## Design - -- General goal: Create a contract (”SignedExecutor”) that validates signatures and executes actions to 1. deploy contracts, 2. create tokens, 3. setup created token parameters 4. mint tokens to the executor of the transaction as a reward -- A creator can create multiple tokens without needing to pay any gas. Each token creation intent is bundled into a signature, which can be executed later by any account. The signature for each token are unordered; they can be executed in any order, and the order they are executed on will determine their token id. -## Contracts - -`Preminter`: Executes commands on the 1155 contract factory, and created 1155 contracts - -Constraints: - * **Contract creation params must be unique** - the combination of creator + metadata uri + name must be unique. The Preminter can only create a single contract for each combination of creator, metadat uri, and name. There must be some sort of validation in the create flow that ensures a contract has not been created with those parameters. - * **For each contract, token parameters must be unique.** The combination of parameters for the token to be created, including metadata uri, max supply, duration, etc **must be unique within each contract.** i.e. a contract cannot have two tokens with the same parameters. This is because we use this combination to ensure that a signature to create the token can only be executed once. An alternative design is to require a unique nonce to be appended to the parameters, which would ensure uniqueness; this would need to be provided by the backend. - -Functions: - * `premint`: takes an [EIP712 signature](https://eips.ethereum.org/EIPS/eip-712) created by a creator, contract and token creation params, and creates a contract if the contract doesn’t exist and creates a new token, or creates a new token on an existing contract if it exists. It then mints a specified quantity of tokens to the executor as a reward. These parameters are the same both if a new contract is created or a token is created on an existing contract. The signature must have been previously created from a hash built from all of the input parameters; the hash can be generated using `premintHashData`. **Each signature can only be executed against once**; this is enforced through uniqueness of the contract creation params, the token creation params, and quantity to mint. - * inputs: - * `contractCreationConfig` - * `contractAdmin` - creator/admin of the contract. **Must match the address of the account that signed the signature** - * `contractURI` - metadata uri of the contract - * `defaultRoyaltyConfiguration` - contract royalty config - * `tokenCreationConfig` - * `tokenURI` - metadata uri of the token to be created - * `tokenMaxSupply` - max supply of the token to be created - * `saleDuration` - how long this token should be on sale for, from the time of the first mint. If 0, duration is infinite - * `maxTokensPerAddress` - max tokens an address can mint - * `pricePerToken` - cost to mint each token - * `uid` - unique id of the token scoped within the contract. Ensures that multiple signatures for a token cannot be executed thus creating two tokens. - * `signature` - signature signed message containing all of the above parameters - * `quantityToMint` - how many of the initial tokens to mint to the executor - -## Functional flow: - -### Diagrams - -Creating a new contract + token: - -![Preminter creation flow](../../uml/generated/gasslessCreate-creation-sequence.svg) -![Preminter creation flow](../../uml/generated/gasslessCreate-creation-activity.svg) - -Collecting: - -![Preminter collection flow](../../uml/generated/gasslessCreate-collecting-sequence.svg) -![Preminter collection flow](../../uml/generated/gasslessCreate-collecting-activity.svg) - -* In the front-end a creator creates a signature for contract and token creation. The signature is created off-chain by the creator's account on a hash of the above said parameters. It there are additional tokens to be created, signatures are created for each token to be created. There must be some validation that a signature with the same parameters has not already been created (see constraints above). This can be done by checking against the uniqueness of the created signature. -* Once the creator has signed the message, a backend service (another db or blockchain) must store these signatures which can be retreived later by a collector. This backend must store both the contract + token creation parameters and the signature. -* A collector lands on a page that loads the signature and contract creation params based on the bytes32 signature. The contract + token creation parameters and signature are loaded from the backend service or a subgraph which loads the previously stored signature. -* The collector account executs the function `premint`, passing the corresponding signature and contract creation params. If the contract has not been created, it is created. A new token is created on that contract, and `quantityToMint` tokens are minted to the executor. - -## Additional caveats - -* The `Preminter` contract is granted the role `PERMISSION_BIT_MINTER` on the 1155 contract, allowing it to create new tokens. -* There are some issues where marketplaces show tx.origin of a transaction as the contract creator, which in this case would show the collector as the contract creator. \ No newline at end of file diff --git a/src/premint/ZoraCreator1155Attribution.sol b/src/premint/ZoraCreator1155Attribution.sol deleted file mode 100644 index 4c7f5cc2e..000000000 --- a/src/premint/ZoraCreator1155Attribution.sol +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import {IMinter1155} from "../interfaces/IMinter1155.sol"; -import {IZoraCreator1155} from "../interfaces/IZoraCreator1155.sol"; -import {ICreatorRoyaltiesControl} from "../interfaces/ICreatorRoyaltiesControl.sol"; -import {ECDSAUpgradeable} from "@zoralabs/openzeppelin-contracts-upgradeable/contracts/utils/cryptography/ECDSAUpgradeable.sol"; -import {ZoraCreatorFixedPriceSaleStrategy} from "../minters/fixed-price/ZoraCreatorFixedPriceSaleStrategy.sol"; - -struct ContractCreationConfig { - // Creator/admin of the created contract. Must match the account that signed the message - address contractAdmin; - // Metadata URI for the created contract - string contractURI; - // Name of the created contract - string contractName; -} - -struct TokenCreationConfig { - // Metadata URI for the created token - string tokenURI; - // Max supply of the created token - uint256 maxSupply; - // Max tokens that can be minted for an address, 0 if unlimited - uint64 maxTokensPerAddress; - // Price per token in eth wei. 0 for a free mint. - uint96 pricePerToken; - // The start time of the mint, 0 for immediate. Prevents signatures from being used until the start time. - uint64 mintStart; - // The duration of the mint, starting from the first mint of this token. 0 for infinite - uint64 mintDuration; - // RoyaltyMintSchedule for created tokens. Every nth token will go to the royalty recipient. - uint32 royaltyMintSchedule; - // RoyaltyBPS for created tokens. The royalty amount in basis points for secondary sales. - uint32 royaltyBPS; - // RoyaltyRecipient for created tokens. The address that will receive the royalty payments. - address royaltyRecipient; - // Fixed price minter address - address fixedPriceMinter; -} - -struct PremintConfig { - // The config for the token to be created - TokenCreationConfig tokenConfig; - // Unique id of the token, used to ensure that multiple signatures can't be used to create the same intended token. - // only one signature per token id, scoped to the contract hash can be executed. - uint32 uid; - // Version of this premint, scoped to the uid and contract. Not used for logic in the contract, but used externally to track the newest version - uint32 version; - // If executing this signature results in preventing any signature with this uid from being minted. - bool deleted; -} - -/// @title Enables a creator to signal intent to create a Zora erc1155 contract or new token on that -/// contract by signing a transaction but not paying gas, and have a third party/collector pay the gas -/// by executing the transaction. Incentivizes the third party to execute the transaction by offering -/// a reward in the form of minted tokens. -/// @author @oveddan -library ZoraCreator1155Attribution { - /* start eip712 functionality */ - string internal constant NAME = "Preminter"; - string internal constant VERSION = "1"; - bytes32 internal constant HASHED_NAME = keccak256(bytes(NAME)); - bytes32 internal constant HASHED_VERSION = keccak256(bytes(VERSION)); - bytes32 internal constant TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - - /** - * @dev Returns the domain separator for the specified chain. - */ - function _domainSeparatorV4(uint256 chainId, address verifyingContract) internal pure returns (bytes32) { - return _buildDomainSeparator(HASHED_NAME, HASHED_VERSION, verifyingContract, chainId); - } - - function _buildDomainSeparator(bytes32 nameHash, bytes32 versionHash, address verifyingContract, uint256 chainId) private pure returns (bytes32) { - return keccak256(abi.encode(TYPE_HASH, nameHash, versionHash, chainId, verifyingContract)); - } - - function _hashTypedDataV4(bytes32 structHash, address verifyingContract, uint256 chainId) private pure returns (bytes32) { - return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(chainId, verifyingContract), structHash); - } - - /* end eip712 functionality */ - - function recoverSigner( - PremintConfig calldata premintConfig, - bytes calldata signature, - address erc1155Contract, - uint256 chainId - ) internal pure returns (address signatory) { - // first validate the signature - the creator must match the signer of the message - return recoverSignerHashed(hashPremint(premintConfig), signature, erc1155Contract, chainId); - } - - function recoverSignerHashed( - bytes32 hashedPremintConfig, - bytes calldata signature, - address erc1155Contract, - uint256 chainId - ) public pure returns (address signatory) { - // first validate the signature - the creator must match the signer of the message - bytes32 digest = _hashTypedDataV4( - hashedPremintConfig, - // here we pass the current contract and chain id, ensuring that the message - // only works for the current chain and contract id - erc1155Contract, - chainId - ); - - signatory = ECDSAUpgradeable.recover(digest, signature); - } - - /// Gets hash data to sign for a premint. Allows specifying a different chain id and contract address so that the signature - /// can be verified on a different chain. - /// @param erc1155Contract Contract address that signature is to be verified against - /// @param chainId Chain id that signature is to be verified on - function premintHashedTypeDataV4(PremintConfig calldata premintConfig, address erc1155Contract, uint256 chainId) external pure returns (bytes32) { - // build the struct hash to be signed - // here we pass the chain id, allowing the message to be signed for another chain - return _hashTypedDataV4(hashPremint(premintConfig), erc1155Contract, chainId); - } - - bytes32 constant ATTRIBUTION_DOMAIN = - keccak256( - "CreatorAttribution(TokenCreationConfig tokenConfig,uint32 uid,uint32 version,bool deleted)TokenCreationConfig(string tokenURI,uint256 maxSupply,uint64 maxTokensPerAddress,uint96 pricePerToken,uint64 mintStart,uint64 mintDuration,uint32 royaltyMintSchedule,uint32 royaltyBPS,address royaltyRecipient,address fixedPriceMinter)" - ); - - function hashPremint(PremintConfig calldata premintConfig) public pure returns (bytes32) { - return - keccak256(abi.encode(ATTRIBUTION_DOMAIN, _hashToken(premintConfig.tokenConfig), premintConfig.uid, premintConfig.version, premintConfig.deleted)); - } - - bytes32 constant TOKEN_DOMAIN = - keccak256( - "TokenCreationConfig(string tokenURI,uint256 maxSupply,uint64 maxTokensPerAddress,uint96 pricePerToken,uint64 mintStart,uint64 mintDuration,uint32 royaltyMintSchedule,uint32 royaltyBPS,address royaltyRecipient,address fixedPriceMinter)" - ); - - function _hashToken(TokenCreationConfig calldata tokenConfig) private pure returns (bytes32) { - return - keccak256( - abi.encode( - TOKEN_DOMAIN, - _stringHash(tokenConfig.tokenURI), - tokenConfig.maxSupply, - tokenConfig.maxTokensPerAddress, - tokenConfig.pricePerToken, - tokenConfig.mintStart, - tokenConfig.mintDuration, - tokenConfig.royaltyMintSchedule, - tokenConfig.royaltyBPS, - tokenConfig.royaltyRecipient, - tokenConfig.fixedPriceMinter - ) - ); - } - - function _stringHash(string calldata value) private pure returns (bytes32) { - return keccak256(bytes(value)); - } - - // todo: move to its own contract - error MintNotYetStarted(); - error PremintDeleted(); - - function validateAndHashPremint(PremintConfig calldata premintConfig) external view returns (bytes32) { - if (premintConfig.tokenConfig.mintStart != 0 && premintConfig.tokenConfig.mintStart > block.timestamp) { - // if the mint start is in the future, then revert - revert MintNotYetStarted(); - } - if (premintConfig.deleted) { - // if the signature says to be deleted, then dont execute any further minting logic; - // return 0 - revert PremintDeleted(); - } - - return hashPremint(premintConfig); - } -} - -// todo: make it consistent. -library PremintTokenSetup { - uint256 constant PERMISSION_BIT_MINTER = 2 ** 2; - - function makeSetupNewTokenCalls( - uint256 newTokenId, - address contractAdmin, - TokenCreationConfig calldata tokenConfig - ) external view returns (bytes[] memory calls) { - calls = new bytes[](3); - - address fixedPriceMinterAddress = tokenConfig.fixedPriceMinter; - // build array of the calls to make - // get setup actions and invoke them - // set up the sales strategy - // first, grant the fixed price sale strategy minting capabilities on the token - // tokenContract.addPermission(newTokenId, address(fixedPriceMinter), PERMISSION_BIT_MINTER); - calls[0] = abi.encodeWithSelector(IZoraCreator1155.addPermission.selector, newTokenId, fixedPriceMinterAddress, PERMISSION_BIT_MINTER); - - // set the sales config on that token - calls[1] = abi.encodeWithSelector( - IZoraCreator1155.callSale.selector, - newTokenId, - IMinter1155(fixedPriceMinterAddress), - abi.encodeWithSelector( - ZoraCreatorFixedPriceSaleStrategy.setSale.selector, - newTokenId, - _buildNewSalesConfig(contractAdmin, tokenConfig.pricePerToken, tokenConfig.maxTokensPerAddress, tokenConfig.mintDuration) - ) - ); - - // set the royalty config on that token: - calls[2] = abi.encodeWithSelector( - IZoraCreator1155.updateRoyaltiesForToken.selector, - newTokenId, - ICreatorRoyaltiesControl.RoyaltyConfiguration({ - royaltyBPS: tokenConfig.royaltyBPS, - royaltyRecipient: tokenConfig.royaltyRecipient, - royaltyMintSchedule: tokenConfig.royaltyMintSchedule - }) - ); - } - - function _buildNewSalesConfig( - address creator, - uint96 pricePerToken, - uint64 maxTokensPerAddress, - uint64 duration - ) private view returns (ZoraCreatorFixedPriceSaleStrategy.SalesConfig memory) { - uint64 saleStart = uint64(block.timestamp); - uint64 saleEnd = duration == 0 ? type(uint64).max : saleStart + duration; - - return - ZoraCreatorFixedPriceSaleStrategy.SalesConfig({ - pricePerToken: pricePerToken, - saleStart: saleStart, - saleEnd: saleEnd, - maxTokensPerAddress: maxTokensPerAddress, - fundsRecipient: creator - }); - } -} diff --git a/src/premint/ZoraCreator1155PremintExecutor.sol b/src/premint/ZoraCreator1155PremintExecutor.sol deleted file mode 100644 index 2d4b93dc6..000000000 --- a/src/premint/ZoraCreator1155PremintExecutor.sol +++ /dev/null @@ -1,212 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import {ICreatorRoyaltiesControl} from "../interfaces/ICreatorRoyaltiesControl.sol"; -import {UUPSUpgradeable} from "@zoralabs/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; -import {Ownable2StepUpgradeable} from "../utils/ownable/Ownable2StepUpgradeable.sol"; -import {IHasContractName} from "../interfaces/IContractMetadata.sol"; -import {IZoraCreator1155} from "../interfaces/IZoraCreator1155.sol"; -import {IZoraCreator1155Errors} from "../interfaces/IZoraCreator1155Errors.sol"; -import {IZoraCreator1155Factory} from "../interfaces/IZoraCreator1155Factory.sol"; -import {SharedBaseConstants} from "../shared/SharedBaseConstants.sol"; -import {ZoraCreatorFixedPriceSaleStrategy} from "../minters/fixed-price/ZoraCreatorFixedPriceSaleStrategy.sol"; -import {IMinter1155} from "../interfaces/IMinter1155.sol"; -import {PremintConfig, ContractCreationConfig, TokenCreationConfig, ZoraCreator1155Attribution} from "./ZoraCreator1155Attribution.sol"; - -/// @title Enables creation of and minting tokens on Zora1155 contracts transactions using eip-712 signatures. -/// Signature must provided by the contract creator, or an account that's permitted to create new tokens on the contract. -/// Mints the first x tokens to the executor of the transaction. -/// Stateless contract that just executes atomic transactions for delegated contract and token creation and minting. -/// @author @oveddan -contract ZoraCreator1155PremintExecutor is Ownable2StepUpgradeable, UUPSUpgradeable, IHasContractName, IZoraCreator1155Errors { - IZoraCreator1155Factory public immutable zora1155Factory; - - /// @notice copied from SharedBaseConstants - uint256 constant CONTRACT_BASE_ID = 0; - /// @dev copied from ZoraCreator1155Impl - uint256 constant PERMISSION_BIT_MINTER = 2 ** 2; - - error MintNotYetStarted(); - error InvalidSignature(); - - constructor(IZoraCreator1155Factory _factory) { - zora1155Factory = _factory; - } - - /// Initialized the upgradeable contract - /// @param _initialOwner The owner of the contract - function initialize(address _initialOwner) public initializer { - __Ownable_init(_initialOwner); - __UUPSUpgradeable_init(); - } - - /// @notice Emitted when a new token is created and minted to the executor of the transaction - /// @param contractAddress address of erc1155 contract that premint was executed against. This contract would have been created by the premint call if it didn't already exist. - /// @param tokenId Created token id as a result of the premint action - /// @param createdNewContract If a new contract was created as a result of the premint action - /// @param uid uid of the signed premint - /// @param contractConfig Configuration of contract that was created, if one was created - /// @param tokenConfig Configuration of token that was created - /// @param minter Address of the minter that executed the premint action - /// @param quantityMinted Quantity of tokens that were minted to the executor of the transaction - event Preminted( - address indexed contractAddress, - uint256 indexed tokenId, - bool indexed createdNewContract, - uint32 uid, - ContractCreationConfig contractConfig, - TokenCreationConfig tokenConfig, - address minter, - uint256 quantityMinted - ); - - /// @notice Creates a new token on the given erc1155 contract on behalf of a creator, and mints x tokens to the executor of this transaction. - /// If the erc1155 contract hasn't been created yet, it will be created with the given config within this same transaction. - /// The creator must sign the intent to create the token, and must have mint new token permission on the erc1155 contract, - /// or match the contract admin on the contract creation config if the contract hasn't been created yet. - /// Contract address of the created contract is deterministically generated from the contract config, this contract's address, and the erc1155 factory contract's address. - /// @param contractConfig Parameters for creating a new erc1155 contract, if one doesn't exist yet. Used to resolve the deterministic contract address. - /// @param premintConfig Parameters for creating the token, and minting the initial x tokens to the executor. - /// @param signature Signature of the creator of the premint. - /// @param quantityToMint How many tokens to mint to the executor of this transaction once the token is created - /// @param mintComment A comment to associate with the mint action - function premint( - ContractCreationConfig calldata contractConfig, - PremintConfig calldata premintConfig, - bytes calldata signature, - uint256 quantityToMint, - string calldata mintComment - ) public payable returns (uint256 newTokenId) { - // get or create the contract with the given params - // contract address is deterministic. - (IZoraCreator1155 tokenContract, bool isNewContract) = _getOrCreateContract(contractConfig); - - // pass the signature and the premint config to the token contract to create the token. - // The token contract will verify the signature and that the signer has permission to create a new token. - // and then create and setup the token using the given token config. - newTokenId = tokenContract.delegateSetupNewToken(premintConfig, signature); - - tokenContract.mint{value: msg.value}( - IMinter1155(premintConfig.tokenConfig.fixedPriceMinter), - newTokenId, - quantityToMint, - abi.encode(msg.sender, mintComment) - ); - - // emit Preminted event - emit Preminted( - address(tokenContract), - newTokenId, - isNewContract, - premintConfig.uid, - contractConfig, - premintConfig.tokenConfig, - msg.sender, - quantityToMint - ); - } - - function _getOrCreateContract(ContractCreationConfig calldata contractConfig) private returns (IZoraCreator1155 tokenContract, bool isNewContract) { - address contractAddress = getContractAddress(contractConfig); - // first we see if the code is already deployed for the contract - isNewContract = contractAddress.code.length == 0; - - if (isNewContract) { - // if address doesnt exist for hash, createi t - tokenContract = _createContract(contractConfig); - } else { - tokenContract = IZoraCreator1155(contractAddress); - } - } - - function _createContract(ContractCreationConfig calldata contractConfig) private returns (IZoraCreator1155 tokenContract) { - // we need to build the setup actions, that must: - bytes[] memory setupActions = new bytes[](0); - - // create the contract via the factory. - address newContractAddresss = zora1155Factory.createContractDeterministic( - contractConfig.contractURI, - contractConfig.contractName, - // default royalty config is empty, since we set it on a token level - ICreatorRoyaltiesControl.RoyaltyConfiguration({royaltyBPS: 0, royaltyRecipient: address(0), royaltyMintSchedule: 0}), - payable(contractConfig.contractAdmin), - setupActions - ); - tokenContract = IZoraCreator1155(newContractAddresss); - } - - /// @notice Gets the deterministic contract address for the given contract creation config. - /// Contract address is generated deterministically from this premint contract's address, the factory's address, and a hash based on the contract uri, contract name, - /// contract admin. - /// @param contractConfig Configuration of the contract to get the address for - function getContractAddress(ContractCreationConfig calldata contractConfig) public view returns (address) { - return - zora1155Factory.deterministicContractAddress(address(this), contractConfig.contractURI, contractConfig.contractName, contractConfig.contractAdmin); - } - - /// Recovers the signer of the given premint config created against the specified zora1155 contract address. - function recoverSigner(PremintConfig calldata premintConfig, address zor1155Address, bytes calldata signature) public view returns (address) { - return ZoraCreator1155Attribution.recoverSigner(premintConfig, signature, zor1155Address, block.chainid); - } - - /// @notice Utility function to determine if a premint contract has been created for a uid of a premint, and if so, - /// What is the token id that was created for the uid. - /// @param contractAddress address of contract to get status for - /// @param uid of premint to check status for - /// @return contractCreated if the 1155 contract has been created - /// @return tokenIdForPremint If the token has been created for the premint, the token id, otherwise 0 - function premintStatus(address contractAddress, uint32 uid) public view returns (bool contractCreated, uint256 tokenIdForPremint) { - if (contractAddress.code.length == 0) { - return (false, 0); - } - return (true, IZoraCreator1155(contractAddress).delegatedTokenId(uid)); - } - - /// @notice Utility function to check if the signature is valid; i.e. the signature can be used to - /// mint a token with the given config. If the contract hasn't been created, then the signer - /// must match the contract admin on the premint config. If it has been created, the signer - /// must have permission to create new tokens on the erc1155 contract. - function isValidSignature( - ContractCreationConfig calldata contractConfig, - PremintConfig calldata premintConfig, - bytes calldata signature - ) public view returns (bool isValid, address contractAddress, address recoveredSigner) { - contractAddress = getContractAddress(contractConfig); - recoveredSigner = recoverSigner(premintConfig, contractAddress, signature); - - if (recoveredSigner == address(0)) { - return (false, contractAddress, address(0)); - } - - // if contract hasn't been created, signer must be the contract admin on the config - if (contractAddress.code.length == 0) { - isValid = recoveredSigner == contractConfig.contractAdmin; - } else { - // if contract has been created, signer must have mint new token permission - isValid = IZoraCreator1155(contractAddress).isAdminOrRole(recoveredSigner, CONTRACT_BASE_ID, PERMISSION_BIT_MINTER); - } - } - - // begin upgrade related functionality - - /// @notice The name of the contract for upgrade purposes - function contractName() external pure returns (string memory) { - return "ZORA 1155 Premint Executor"; - } - - // upgrade functionality - error UpgradeToMismatchedContractName(string expected, string actual); - - /// @notice Ensures the caller is authorized to upgrade the contract - /// @dev This function is called in `upgradeTo` & `upgradeToAndCall` - /// @param _newImpl The new implementation address - function _authorizeUpgrade(address _newImpl) internal override onlyOwner { - if (!_equals(IHasContractName(_newImpl).contractName(), this.contractName())) { - revert UpgradeToMismatchedContractName(this.contractName(), IHasContractName(_newImpl).contractName()); - } - } - - function _equals(string memory a, string memory b) internal pure returns (bool) { - return (keccak256(bytes(a)) == keccak256(bytes(b))); - } -} diff --git a/test/minters/redeem/ZoraCreatorRedeemMinterFactory.t.sol b/test/minters/redeem/ZoraCreatorRedeemMinterFactory.t.sol index 37d9ccfb1..738feab88 100644 --- a/test/minters/redeem/ZoraCreatorRedeemMinterFactory.t.sol +++ b/test/minters/redeem/ZoraCreatorRedeemMinterFactory.t.sol @@ -9,7 +9,7 @@ import {ProtocolRewards} from "@zoralabs/protocol-rewards/src/ProtocolRewards.so import {ZoraCreator1155Impl} from "../../../src/nft/ZoraCreator1155Impl.sol"; import {Zora1155} from "../../../src/proxies/Zora1155.sol"; import {IMinter1155} from "../../../src/interfaces/IMinter1155.sol"; -import {IZoraCreator1155Errors} from "../../../src/interfaces/IZoraCreator1155Errors.sol"; +import {IZoraCreator1155} from "../../../src/interfaces/IZoraCreator1155.sol"; import {IRenderer1155} from "../../../src/interfaces/IRenderer1155.sol"; import {ICreatorRoyaltiesControl} from "../../../src/interfaces/ICreatorRoyaltiesControl.sol"; import {IZoraCreator1155Factory} from "../../../src/interfaces/IZoraCreator1155Factory.sol"; @@ -25,19 +25,19 @@ contract ZoraCreatorRedeemMinterFactoryTest is Test { address payable internal factoryAdmin = payable(address(0x888)); address internal zora; -// event RedeemMinterDeployed(address indexed creatorContract, address indexed minterContract); + event RedeemMinterDeployed(address indexed creatorContract, address indexed minterContract); -// function setUp() public { -// zora = makeAddr("zora"); -// bytes[] memory emptyData = new bytes[](0); -// protocolRewards = new ProtocolRewards(); -// ZoraCreator1155Impl targetImpl = new ZoraCreator1155Impl(0, zora, address(0), address(protocolRewards)); -// Zora1155 proxy = new Zora1155(address(targetImpl)); -// target = ZoraCreator1155Impl(address(proxy)); -// target.initialize("test", "test", ICreatorRoyaltiesControl.RoyaltyConfiguration(0, 0, address(0)), tokenAdmin, emptyData); + function setUp() public { + zora = makeAddr("zora"); + bytes[] memory emptyData = new bytes[](0); + protocolRewards = new ProtocolRewards(); + ZoraCreator1155Impl targetImpl = new ZoraCreator1155Impl(0, zora, address(0), address(protocolRewards)); + Zora1155 proxy = new Zora1155(address(targetImpl)); + target = ZoraCreator1155Impl(address(proxy)); + target.initialize("test", "test", ICreatorRoyaltiesControl.RoyaltyConfiguration(0, 0, address(0)), tokenAdmin, emptyData); -// minterFactory = new ZoraCreatorRedeemMinterFactory(); -// } + minterFactory = new ZoraCreatorRedeemMinterFactory(); + } function test_contractVersion() public { assertEq(minterFactory.contractVersion(), "1.1.0"); @@ -51,32 +51,30 @@ contract ZoraCreatorRedeemMinterFactoryTest is Test { emit RedeemMinterDeployed(address(target), predictedAddress); target.callSale(0, minterFactory, abi.encodeWithSelector(ZoraCreatorRedeemMinterFactory.createMinterIfNoneExists.selector, 0)); vm.stopPrank(); - } -} -// ZoraCreatorRedeemMinterStrategy minter = ZoraCreatorRedeemMinterStrategy(predictedAddress); -// assertTrue(address(minter).code.length > 0); -// } + ZoraCreatorRedeemMinterStrategy minter = ZoraCreatorRedeemMinterStrategy(predictedAddress); + assertTrue(address(minter).code.length > 0); + } -// function test_createMinterRequiresIZoraCreator1155Caller() public { -// ERC1155PresetMinterPauser randomToken = new ERC1155PresetMinterPauser("https://uri.com"); + function test_createMinterRequiresIZoraCreator1155Caller() public { + ERC1155PresetMinterPauser randomToken = new ERC1155PresetMinterPauser("https://uri.com"); -// vm.expectRevert(abi.encodeWithSignature("CallerNotZoraCreator1155()")); -// vm.prank(address(randomToken)); -// minterFactory.createMinterIfNoneExists(); -// } + vm.expectRevert(abi.encodeWithSignature("CallerNotZoraCreator1155()")); + vm.prank(address(randomToken)); + minterFactory.createMinterIfNoneExists(); + } -// function test_getDeployedMinterForCreatorContract() public { -// vm.prank(address(target)); -// minterFactory.createMinterIfNoneExists(); -// address minterAddress = minterFactory.predictMinterAddress(address(target)); + function test_getDeployedMinterForCreatorContract() public { + vm.prank(address(target)); + minterFactory.createMinterIfNoneExists(); + address minterAddress = minterFactory.predictMinterAddress(address(target)); -// assertEq(minterAddress, minterFactory.getDeployedRedeemMinterForCreatorContract(address(target))); -// } + assertEq(minterAddress, minterFactory.getDeployedRedeemMinterForCreatorContract(address(target))); + } -// function test_supportsInterface() public { -// assertTrue(minterFactory.supportsInterface(0x01ffc9a7)); // ERC165 -// assertTrue(minterFactory.supportsInterface(type(IMinter1155).interfaceId)); -// assertTrue(!minterFactory.supportsInterface(0x6467a6fc)); // old IMinter1155 -// } -// } + function test_supportsInterface() public { + assertTrue(minterFactory.supportsInterface(0x01ffc9a7)); // ERC165 + assertTrue(minterFactory.supportsInterface(type(IMinter1155).interfaceId)); + assertTrue(!minterFactory.supportsInterface(0x6467a6fc)); // old IMinter1155 + } +} diff --git a/test/minters/redeem/ZoraCreatorRedeemMinterStrategy.t.sol b/test/minters/redeem/ZoraCreatorRedeemMinterStrategy.t.sol index 315b12455..e2b95c56f 100644 --- a/test/minters/redeem/ZoraCreatorRedeemMinterStrategy.t.sol +++ b/test/minters/redeem/ZoraCreatorRedeemMinterStrategy.t.sol @@ -8,7 +8,7 @@ import {ERC1155PresetMinterPauser} from "@openzeppelin/contracts/token/ERC1155/p import {ProtocolRewards} from "@zoralabs/protocol-rewards/src/ProtocolRewards.sol"; import {ZoraCreator1155Impl} from "../../../src/nft/ZoraCreator1155Impl.sol"; import {Zora1155} from "../../../src/proxies/Zora1155.sol"; -import {IZoraCreator1155Errors} from "../../../src/interfaces/IZoraCreator1155Errors.sol"; +import {IZoraCreator1155} from "../../../src/interfaces/IZoraCreator1155.sol"; import {IRenderer1155} from "../../../src/interfaces/IRenderer1155.sol"; import {ICreatorRoyaltiesControl} from "../../../src/interfaces/ICreatorRoyaltiesControl.sol"; import {IZoraCreator1155Factory} from "../../../src/interfaces/IZoraCreator1155Factory.sol";