diff --git a/.changeset/violet-starfishes-visit.md b/.changeset/violet-starfishes-visit.md index 22429140a..b645746be 100644 --- a/.changeset/violet-starfishes-visit.md +++ b/.changeset/violet-starfishes-visit.md @@ -2,9 +2,12 @@ "@zoralabs/zora-1155-contracts": minor --- -Premint v2 - for add new signature, where createReferral can be specified. ZoraCreator1155PremintExecutor recognizes new version of the signature, and still works with the v1 (legacy) version of the signature. 1155 contract has been updated to now take abi encoded premint config, premint config version, and send it to an external library to decode the config, the signer, and setup actions. +Premint v2 - for add new signature, where createReferral can be specified. ZoraCreator1155PremintExecutor recognizes new version of the signature, and still works with the v1 (legacy) version of the signature. ZoraCreator1155PremintExecutor can now validate signatures by passing it the contract address, instead of needing to pass the full contract creation config, enabling it to validate signatures for 1155 contracts that were not created via the premint executor contract. 1155 contract has been updated to now take abi encoded premint config, premint config version, and send it to an external library to decode the config, the signer, and setup actions. changes to `ZoraCreator1155PremintExecutorImpl`: * new function `premintV1` - takes a premint v1 signature and executed a premint, with added functionality of being able to specify mint referral and mint recipient * new function `premintV2` - takes a premint v2 signature and executes a premint, with being able to specify mint referral and mint recipient -* deprecated function `premint` - call `premintV1` instead \ No newline at end of file +* new function `isValidSignatureV1` - takes an 1155 address, contract admin, premint v1 config and signature, and validates the signature. Can be used for 1155 contracts that were not created via the premint executor contract. +* new function `isValidSignatureV2` - takes an 1155 address, contract admin, premint v2 config and signature, and validates the signature. Can be used for 1155 contracts that were not created via the premint executor contract. +* deprecated function `premint` - call `premintV1` instead +* deprecated function `isValidSignature` - call `isValidSignatureV1` instead \ No newline at end of file diff --git a/packages/1155-contracts/src/delegation/ZoraCreator1155Attribution.sol b/packages/1155-contracts/src/delegation/ZoraCreator1155Attribution.sol index b50e76f6a..d20f19086 100644 --- a/packages/1155-contracts/src/delegation/ZoraCreator1155Attribution.sol +++ b/packages/1155-contracts/src/delegation/ZoraCreator1155Attribution.sol @@ -400,7 +400,7 @@ library DelegatedTokenCreation { ); (params, tokenSetupActions) = _recoverDelegatedTokenSetup(premintConfig, newTokenId); - } else { + } else if (premintVersion == ZoraCreator1155Attribution.HASHED_VERSION_2) { PremintConfigV2 memory premintConfig = abi.decode(premintConfigEncoded, (PremintConfigV2)); creatorAttribution = _recoverCreatorAttribution( @@ -411,6 +411,8 @@ library DelegatedTokenCreation { ); (params, tokenSetupActions) = _recoverDelegatedTokenSetup(premintConfig, newTokenId); + } else { + revert IZoraCreator1155Errors.InvalidSignatureVersion(); } } diff --git a/packages/1155-contracts/src/delegation/ZoraCreator1155PremintExecutorImpl.sol b/packages/1155-contracts/src/delegation/ZoraCreator1155PremintExecutorImpl.sol index 0e5ea5c30..d739f2cd4 100644 --- a/packages/1155-contracts/src/delegation/ZoraCreator1155PremintExecutorImpl.sol +++ b/packages/1155-contracts/src/delegation/ZoraCreator1155PremintExecutorImpl.sol @@ -14,7 +14,7 @@ import {IMinter1155} from "../interfaces/IMinter1155.sol"; import {ERC1155DelegationStorageV1} from "../delegation/ERC1155DelegationStorageV1.sol"; import {ZoraCreator1155PremintExecutorImplLib} from "./ZoraCreator1155PremintExecutorImplLib.sol"; import {PremintEncoding, ZoraCreator1155Attribution, ContractCreationConfig, PremintConfig, PremintConfigV2, TokenCreationConfig, TokenCreationConfigV2} from "./ZoraCreator1155Attribution.sol"; -import {IZoraCreator1155PremintExecutor, ILegacyZoraCreator1155PremintExecutor} from "../interfaces/IZoraCreator1155PremintExecutor.sol"; +import {IZoraCreator1155PremintExecutor} from "../interfaces/IZoraCreator1155PremintExecutor.sol"; struct MintArguments { // which account should receive the tokens minted. If set to address(0), then defaults to the msg.sender @@ -30,7 +30,6 @@ struct MintArguments { /// Mints the first x tokens to the executor of the transaction. /// @author @oveddan contract ZoraCreator1155PremintExecutorImpl is - ILegacyZoraCreator1155PremintExecutor, IZoraCreator1155PremintExecutor, Ownable2StepUpgradeable, UUPSUpgradeable, diff --git a/packages/1155-contracts/src/delegation/ZoraCreator1155PremintExecutorImplLib.sol b/packages/1155-contracts/src/delegation/ZoraCreator1155PremintExecutorImplLib.sol index ce5fcef75..019b3117d 100644 --- a/packages/1155-contracts/src/delegation/ZoraCreator1155PremintExecutorImplLib.sol +++ b/packages/1155-contracts/src/delegation/ZoraCreator1155PremintExecutorImplLib.sol @@ -1,12 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {ContractCreationConfig} from "./ZoraCreator1155Attribution.sol"; +import {ContractCreationConfig, PremintConfig} from "./ZoraCreator1155Attribution.sol"; import {IZoraCreator1155} from "../interfaces/IZoraCreator1155.sol"; import {IZoraCreator1155Factory} from "../interfaces/IZoraCreator1155Factory.sol"; import {ICreatorRoyaltiesControl} from "../interfaces/ICreatorRoyaltiesControl.sol"; import {IMinter1155} from "../interfaces/IMinter1155.sol"; import {IZoraCreator1155PremintExecutor} from "../interfaces/IZoraCreator1155PremintExecutor.sol"; +import {IZoraCreator1155DelegatedCreation} from "../interfaces/IZoraCreator1155DelegatedCreation.sol"; + +interface ILegacyZoraCreator1155DelegatedMinter { + function delegateSetupNewToken(PremintConfig calldata premintConfig, bytes calldata signature, address sender) external returns (uint256 newTokenId); +} library ZoraCreator1155PremintExecutorImplLib { function getOrCreateContract( @@ -60,6 +65,21 @@ library ZoraCreator1155PremintExecutorImplLib { return abi.decode(mintArguments, (address, string)); } + function legacySetupNewToken(address contractAddress, bytes memory encodedPremintConfig, bytes calldata signature) private returns (uint256) { + // for use when the erc1155 contract does not support the new delegateSetupNewToken interface, where it expects + // a PremintConfig as an argument. + + // decode the PremintConfig from the encoded bytes. + PremintConfig memory premintConfig = abi.decode(encodedPremintConfig, (PremintConfig)); + + // call the legacy version of the delegateSetupNewToken function. + return ILegacyZoraCreator1155DelegatedMinter(contractAddress).delegateSetupNewToken(premintConfig, signature, msg.sender); + } + + function supportsNewPremintInterface(address contractAddress) internal view returns (bool) { + return IZoraCreator1155(contractAddress).supportsInterface(type(IZoraCreator1155DelegatedCreation).interfaceId); + } + function premint( IZoraCreator1155Factory zora1155Factory, ContractCreationConfig calldata contractConfig, @@ -74,10 +94,19 @@ library ZoraCreator1155PremintExecutorImplLib { // contract address is deterministic. (IZoraCreator1155 tokenContract, bool isNewContract) = getOrCreateContract(zora1155Factory, 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. - uint256 newTokenId = tokenContract.delegateSetupNewToken(encodedPremintConfig, premintVersion, signature, msg.sender); + uint256 newTokenId; + + if (supportsNewPremintInterface(address(tokenContract))) { + // if the contract supports the new interface, we can use it to create the token. + + // 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(encodedPremintConfig, premintVersion, signature, msg.sender); + } else { + // otherwise, we need to use the legacy interface. + newTokenId = legacySetupNewToken(address(tokenContract), encodedPremintConfig, signature); + } _performMint(tokenContract, fixedPriceMinter, newTokenId, quantityToMint, mintArguments); diff --git a/packages/1155-contracts/src/interfaces/IZoraCreator1155.sol b/packages/1155-contracts/src/interfaces/IZoraCreator1155.sol index f3d2c3d99..e34a39b03 100644 --- a/packages/1155-contracts/src/interfaces/IZoraCreator1155.sol +++ b/packages/1155-contracts/src/interfaces/IZoraCreator1155.sol @@ -10,7 +10,7 @@ import {IMinter1155} from "../interfaces/IMinter1155.sol"; import {IOwnable} from "../interfaces/IOwnable.sol"; import {IVersionedContract} from "./IVersionedContract.sol"; import {ICreatorRoyaltiesControl} from "../interfaces/ICreatorRoyaltiesControl.sol"; -import {PremintConfigV2} from "../delegation/ZoraCreator1155Attribution.sol"; +import {IZoraCreator1155DelegatedCreation} from "./IZoraCreator1155DelegatedCreation.sol"; /* @@ -36,7 +36,14 @@ import {PremintConfigV2} from "../delegation/ZoraCreator1155Attribution.sol"; /// @notice Main interface for the ZoraCreator1155 contract /// @author @iainnash / @tbtstl -interface IZoraCreator1155 is IZoraCreator1155TypesV1, IZoraCreator1155Errors, IVersionedContract, IOwnable, IERC1155MetadataURIUpgradeable { +interface IZoraCreator1155 is + IZoraCreator1155TypesV1, + IZoraCreator1155Errors, + IVersionedContract, + IOwnable, + IERC1155MetadataURIUpgradeable, + IZoraCreator1155DelegatedCreation +{ function PERMISSION_BIT_ADMIN() external returns (uint256); function PERMISSION_BIT_MINTER() external returns (uint256); @@ -61,7 +68,6 @@ interface IZoraCreator1155 is IZoraCreator1155TypesV1, IZoraCreator1155Errors, I 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 CreatorAttribution(bytes32 structHash, string domainName, string version, address creator, bytes signature); /// @notice Only allow minting one token id at time /// @dev Mint contract function that calls the underlying sales function for commands @@ -84,13 +90,6 @@ interface IZoraCreator1155 is IZoraCreator1155TypesV1, IZoraCreator1155Errors, I /// @param maxSupply maxSupply for the token, set to 0 for open edition function setupNewToken(string memory tokenURI, uint256 maxSupply) external returns (uint256 tokenId); - function delegateSetupNewToken( - bytes memory premintConfigEncoded, - bytes32 premintVersion, - bytes calldata signature, - address sender - ) external returns (uint256 newTokenId); - function updateTokenURI(uint256 tokenId, string memory _newURI) external; function updateContractMetadata(string memory _newURI, string memory _newName) external; diff --git a/packages/1155-contracts/src/interfaces/IZoraCreator1155DelegatedCreation.sol b/packages/1155-contracts/src/interfaces/IZoraCreator1155DelegatedCreation.sol new file mode 100644 index 000000000..85c49eaba --- /dev/null +++ b/packages/1155-contracts/src/interfaces/IZoraCreator1155DelegatedCreation.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +interface IZoraCreator1155DelegatedCreation { + event CreatorAttribution(bytes32 structHash, string domainName, string version, address creator, bytes signature); + + function delegateSetupNewToken( + bytes memory premintConfigEncoded, + bytes32 premintVersion, + bytes calldata signature, + address sender + ) external returns (uint256 newTokenId); +} diff --git a/packages/1155-contracts/src/interfaces/IZoraCreator1155Errors.sol b/packages/1155-contracts/src/interfaces/IZoraCreator1155Errors.sol index 153cde73c..5f6179601 100644 --- a/packages/1155-contracts/src/interfaces/IZoraCreator1155Errors.sol +++ b/packages/1155-contracts/src/interfaces/IZoraCreator1155Errors.sol @@ -30,4 +30,6 @@ interface IZoraCreator1155Errors { error MintNotYetStarted(); error PremintDeleted(); + + error InvalidSignatureVersion(); } diff --git a/packages/1155-contracts/src/interfaces/IZoraCreator1155PremintExecutor.sol b/packages/1155-contracts/src/interfaces/IZoraCreator1155PremintExecutor.sol index b66df3af3..fbc449ed0 100644 --- a/packages/1155-contracts/src/interfaces/IZoraCreator1155PremintExecutor.sol +++ b/packages/1155-contracts/src/interfaces/IZoraCreator1155PremintExecutor.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.17; import {PremintEncoding, ZoraCreator1155Attribution, ContractCreationConfig, PremintConfig, PremintConfigV2, TokenCreationConfig, TokenCreationConfigV2} from "../delegation/ZoraCreator1155Attribution.sol"; +import {IOwnable2StepUpgradeable} from "../utils/ownable/IOwnable2StepUpgradeable.sol"; import {IZoraCreator1155Factory} from "./IZoraCreator1155Factory.sol"; // interface for legacy v1 of premint executor methods @@ -62,7 +63,12 @@ interface IZoraCreator1155PremintExecutorV2 { ) external view returns (bool isValid, address recoveredSigner); } -interface IZoraCreator1155PremintExecutor is IZoraCreator1155PremintExecutorV1, IZoraCreator1155PremintExecutorV2 { +interface IZoraCreator1155PremintExecutor is + ILegacyZoraCreator1155PremintExecutor, + IZoraCreator1155PremintExecutorV1, + IZoraCreator1155PremintExecutorV2, + IOwnable2StepUpgradeable +{ struct MintArguments { address mintRecipient; string mintComment; diff --git a/packages/1155-contracts/src/nft/ZoraCreator1155Impl.sol b/packages/1155-contracts/src/nft/ZoraCreator1155Impl.sol index d09db27ae..7ee7c4d3c 100644 --- a/packages/1155-contracts/src/nft/ZoraCreator1155Impl.sol +++ b/packages/1155-contracts/src/nft/ZoraCreator1155Impl.sol @@ -32,6 +32,7 @@ import {TransferHelperUtils} from "../utils/TransferHelperUtils.sol"; import {ZoraCreator1155StorageV1} from "./ZoraCreator1155StorageV1.sol"; import {IZoraCreator1155Errors} from "../interfaces/IZoraCreator1155Errors.sol"; import {ERC1155DelegationStorageV1} from "../delegation/ERC1155DelegationStorageV1.sol"; +import {IZoraCreator1155DelegatedCreation} from "../interfaces/IZoraCreator1155DelegatedCreation.sol"; import {ZoraCreator1155Attribution, DecodedCreatorAttribution, PremintTokenSetup, PremintConfig, PremintConfigV2, DelegatedTokenCreation, DelegatedTokenSetup} from "../delegation/ZoraCreator1155Attribution.sol"; /// Imagine. Mint. Enjoy. @@ -564,7 +565,11 @@ contract ZoraCreator1155Impl is function supportsInterface( bytes4 interfaceId ) public view virtual override(CreatorRoyaltiesControl, ERC1155Upgradeable, IERC165Upgradeable) returns (bool) { - return super.supportsInterface(interfaceId) || interfaceId == type(IZoraCreator1155).interfaceId || ERC1155Upgradeable.supportsInterface(interfaceId); + return + super.supportsInterface(interfaceId) || + interfaceId == type(IZoraCreator1155).interfaceId || + ERC1155Upgradeable.supportsInterface(interfaceId) || + interfaceId == type(IZoraCreator1155DelegatedCreation).interfaceId; } /// Generic 1155 function overrides /// diff --git a/packages/1155-contracts/test/fixtures/Zora1155FactoryFixtures.sol b/packages/1155-contracts/test/fixtures/Zora1155FactoryFixtures.sol index d6ccfae24..6bfa5a533 100644 --- a/packages/1155-contracts/test/fixtures/Zora1155FactoryFixtures.sol +++ b/packages/1155-contracts/test/fixtures/Zora1155FactoryFixtures.sol @@ -10,11 +10,13 @@ import {Zora1155Factory} from "../../src/proxies/Zora1155Factory.sol"; import {ZoraCreator1155FactoryImpl} from "../../src/factory/ZoraCreator1155FactoryImpl.sol"; import {ProtocolRewards} from "@zoralabs/protocol-rewards/src/ProtocolRewards.sol"; import {ProxyShim} from "../../src/utils/ProxyShim.sol"; +import {IUpgradeGate} from "../../src/interfaces/IUpgradeGate.sol"; +import {UpgradeGate} from "../../src/upgrades/UpgradeGate.sol"; library Zora1155FactoryFixtures { - function setupZora1155Impl(address zora, Zora1155Factory factoryProxy) internal returns (ZoraCreator1155Impl) { + function setupZora1155Impl(address zora, IUpgradeGate upgradeGate) internal returns (ZoraCreator1155Impl) { ProtocolRewards rewards = new ProtocolRewards(); - return new ZoraCreator1155Impl(zora, address(factoryProxy), address(rewards)); + return new ZoraCreator1155Impl(zora, address(upgradeGate), address(rewards)); } function upgradeFactoryProxyToUse1155( @@ -36,13 +38,23 @@ library Zora1155FactoryFixtures { factoryProxy = new Zora1155Factory(factoryShimAddress, ""); } + function setupNew1155AndFactory( + address zora, + IUpgradeGate upgradeGate, + IMinter1155 fixedPriceMinter + ) internal returns (ZoraCreator1155Impl zoraCreator1155Impl, ZoraCreator1155FactoryImpl factoryImpl) { + zoraCreator1155Impl = setupZora1155Impl(zora, upgradeGate); + factoryImpl = new ZoraCreator1155FactoryImpl(zoraCreator1155Impl, IMinter1155(address(1)), fixedPriceMinter, IMinter1155(address(3))); + } + function setup1155AndFactoryProxy( address zora, address deployer - ) internal returns (ZoraCreator1155Impl zoraCreator1155Impl, IMinter1155 fixedPriceMinter, Zora1155Factory factoryProxy) { + ) internal returns (ZoraCreator1155Impl zoraCreator1155Impl, IMinter1155 fixedPriceMinter, Zora1155Factory factoryProxy, IUpgradeGate upgradeGate) { factoryProxy = setupFactoryProxy(deployer); fixedPriceMinter = new ZoraCreatorFixedPriceSaleStrategy(); - zoraCreator1155Impl = setupZora1155Impl(zora, factoryProxy); + upgradeGate = new UpgradeGate(); + zoraCreator1155Impl = setupZora1155Impl(zora, upgradeGate); upgradeFactoryProxyToUse1155(factoryProxy, zoraCreator1155Impl, fixedPriceMinter, deployer); } } diff --git a/packages/1155-contracts/test/fixtures/Zora1155PremintFixtures.sol b/packages/1155-contracts/test/fixtures/Zora1155PremintFixtures.sol index ab249e05c..f3aa9b309 100644 --- a/packages/1155-contracts/test/fixtures/Zora1155PremintFixtures.sol +++ b/packages/1155-contracts/test/fixtures/Zora1155PremintFixtures.sol @@ -9,7 +9,7 @@ import {Zora1155Factory} from "../../src/proxies/Zora1155Factory.sol"; import {ZoraCreator1155FactoryImpl} from "../../src/factory/ZoraCreator1155FactoryImpl.sol"; import {ProtocolRewards} from "@zoralabs/protocol-rewards/src/ProtocolRewards.sol"; import {ProxyShim} from "../../src/utils/ProxyShim.sol"; -import {ContractCreationConfig, TokenCreationConfigV2} from "../../src/delegation/ZoraCreator1155Attribution.sol"; +import {PremintConfig, ContractCreationConfig, TokenCreationConfigV2, TokenCreationConfig} from "../../src/delegation/ZoraCreator1155Attribution.sol"; library Zora1155PremintFixtures { function makeDefaultContractCreationConfig(address contractAdmin) internal pure returns (ContractCreationConfig memory) { @@ -51,4 +51,26 @@ library Zora1155PremintFixtures { createReferral: createReferral }); } + + function makeDefaultV1PremintConfig(IMinter1155 fixedPriceMinter, address royaltyRecipient) internal pure returns (PremintConfig memory) { + // make a v1 premint config + return + PremintConfig({ + tokenConfig: TokenCreationConfig({ + tokenURI: "blah.token", + maxSupply: 10, + maxTokensPerAddress: 5, + pricePerToken: 0, + mintStart: 0, + mintDuration: 0, + fixedPriceMinter: address(fixedPriceMinter), + royaltyRecipient: royaltyRecipient, + royaltyBPS: 10, + royaltyMintSchedule: 0 + }), + uid: 100, + version: 0, + deleted: false + }); + } } diff --git a/packages/1155-contracts/test/premint/Zora1155PremintExecutorProxy.t.sol b/packages/1155-contracts/test/premint/Zora1155PremintExecutorProxy.t.sol index 2aac3c870..380131ac6 100644 --- a/packages/1155-contracts/test/premint/Zora1155PremintExecutorProxy.t.sol +++ b/packages/1155-contracts/test/premint/Zora1155PremintExecutorProxy.t.sol @@ -11,13 +11,15 @@ import {ZoraCreator1155PremintExecutorImpl} from "../../src/delegation/ZoraCreat import {Zora1155Factory} from "../../src/proxies/Zora1155Factory.sol"; import {IMinter1155} from "../../src/interfaces/IMinter1155.sol"; import {ProxyShim} from "../../src/utils/ProxyShim.sol"; -import {ZoraCreator1155Attribution, ContractCreationConfig, TokenCreationConfigV2, PremintConfigV2} from "../../src/delegation/ZoraCreator1155Attribution.sol"; +import {ZoraCreator1155Attribution, ContractCreationConfig, TokenCreationConfigV2, PremintConfigV2, PremintConfig} from "../../src/delegation/ZoraCreator1155Attribution.sol"; import {IOwnable2StepUpgradeable} from "../../src/utils/ownable/IOwnable2StepUpgradeable.sol"; +import {ForkDeploymentConfig, Deployment, ChainConfig} from "../../src/deployment/DeploymentConfig.sol"; import {IHasContractName} from "../../src/interfaces/IContractMetadata.sol"; import {ZoraCreator1155PremintExecutorImplLib} from "../../src/delegation/ZoraCreator1155PremintExecutorImplLib.sol"; -import {IZoraCreator1155PremintExecutor} from "../../src/interfaces/IZoraCreator1155PremintExecutor.sol"; +import {IUpgradeGate} from "../../src/interfaces/IUpgradeGate.sol"; +import {IZoraCreator1155PremintExecutor, ILegacyZoraCreator1155PremintExecutor} from "../../src/interfaces/IZoraCreator1155PremintExecutor.sol"; -contract Zora1155PremintExecutorProxyTest is Test, IHasContractName { +contract Zora1155PremintExecutorProxyTest is Test, ForkDeploymentConfig, IHasContractName { address internal owner; uint256 internal creatorPrivateKey; address internal creator; @@ -37,7 +39,7 @@ contract Zora1155PremintExecutorProxyTest is Test, IHasContractName { (creator, creatorPrivateKey) = makeAddrAndKey("creator"); vm.startPrank(zora); - (, , factoryProxy) = Zora1155FactoryFixtures.setup1155AndFactoryProxy(zora, zora); + (, , factoryProxy, ) = Zora1155FactoryFixtures.setup1155AndFactoryProxy(zora, zora); factoryAtProxy = ZoraCreator1155FactoryImpl(address(factoryProxy)); vm.stopPrank(); @@ -70,20 +72,14 @@ contract Zora1155PremintExecutorProxyTest is Test, IHasContractName { address deterministicAddress = preminterAtProxy.getContractAddress(contractConfig); // sign the premint - bytes32 structHash = ZoraCreator1155Attribution.hashPremint(premintConfig); - bytes32 digest = ZoraCreator1155Attribution.premintHashedTypeDataV4( - structHash, - deterministicAddress, + bytes memory signature = _signPremint( + ZoraCreator1155Attribution.hashPremint(premintConfig), ZoraCreator1155Attribution.HASHED_VERSION_2, - block.chainid + deterministicAddress ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(creatorPrivateKey, digest); - uint256 quantityToMint = 1; - bytes memory signature = abi.encodePacked(r, s, v); - // execute the premint vm.deal(collector, mintFeeAmount); vm.prank(collector); @@ -121,4 +117,87 @@ contract Zora1155PremintExecutorProxyTest is Test, IHasContractName { vm.prank(owner); preminterAtProxy.upgradeTo(address(newImplementation)); } + + function test_canExecutePremint_onOlderVersionOf1155() external { + vm.createSelectFork("zora", 5_000_000); + + // 1. execute premint using older version of proxy, this will create 1155 contract using the legacy interface + Deployment memory deployment = getDeployment(); + ChainConfig memory chainConfig = getChainConfig(); + + // get premint and factory proxies from forked deployments + ZoraCreator1155PremintExecutorImpl forkedPreminterProxy = ZoraCreator1155PremintExecutorImpl(deployment.preminterProxy); + ZoraCreator1155FactoryImpl forkedFactoryAtProxy = ZoraCreator1155FactoryImpl(address(forkedPreminterProxy.zora1155Factory())); + IMinter1155 fixedPriceMinter = forkedFactoryAtProxy.fixedPriceMinter(); + + assertFalse(deployment.factoryProxy.code.length == 0, "factory proxy code should be deployed"); + + // build and sign v1 premint config + ContractCreationConfig memory contractConfig = Zora1155PremintFixtures.makeDefaultContractCreationConfig(creator); + address deterministicAddress = forkedPreminterProxy.getContractAddress(contractConfig); + PremintConfig memory premintConfig = Zora1155PremintFixtures.makeDefaultV1PremintConfig(fixedPriceMinter, creator); + + bytes memory signature = _signPremint( + ZoraCreator1155Attribution.hashPremint(premintConfig), + ZoraCreator1155Attribution.HASHED_VERSION_1, + deterministicAddress + ); + + // create 1155 contract via premint, using legacy interface + uint256 quantityToMint = 1; + vm.deal(collector, mintFeeAmount); + vm.prank(collector); + + uint256 tokenId = ILegacyZoraCreator1155PremintExecutor(forkedPreminterProxy).premint{value: mintFeeAmount}( + contractConfig, + premintConfig, + signature, + quantityToMint, + "yo" + ); + + // sanity check, make sure the token was minted + assertEq(tokenId, 1); + + // 2. upgrade premint executor and factory to current version + // create new factory proxy implementation + (, ZoraCreator1155FactoryImpl newFactoryVersion) = Zora1155FactoryFixtures.setupNew1155AndFactory( + zora, + IUpgradeGate(deployment.upgradeGate), + fixedPriceMinter + ); + + // upgrade factory proxy + address upgradeOwner = chainConfig.factoryOwner; + vm.prank(upgradeOwner); + forkedFactoryAtProxy.upgradeTo(address(newFactoryVersion)); + // upgrade preminter + ZoraCreator1155PremintExecutorImpl newImplementation = new ZoraCreator1155PremintExecutorImpl(forkedFactoryAtProxy); + vm.prank(upgradeOwner); + forkedPreminterProxy.upgradeTo(address(newImplementation)); + + // 3. create premint on old version of contract using new version of preminter + uint32 existingUid = premintConfig.uid; + premintConfig = Zora1155PremintFixtures.makeDefaultV1PremintConfig(fixedPriceMinter, creator); + premintConfig.uid = existingUid + 1; + signature = _signPremint(ZoraCreator1155Attribution.hashPremint(premintConfig), ZoraCreator1155Attribution.HASHED_VERSION_1, deterministicAddress); + + // execute the premint + vm.deal(collector, mintFeeAmount); + vm.prank(collector); + // now premint using the new method - it should still work + tokenId = forkedPreminterProxy.premintV1{value: mintFeeAmount}(contractConfig, premintConfig, signature, quantityToMint, defaultMintArguments).tokenId; + + // sanity check, make sure token was minted and has a new token id + assertEq(tokenId, 2); + } + + function _signPremint(bytes32 structHash, bytes32 premintVersion, address contractAddress) private view returns (bytes memory signature) { + // sign the premint + bytes32 digest = ZoraCreator1155Attribution.premintHashedTypeDataV4(structHash, contractAddress, premintVersion, block.chainid); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(creatorPrivateKey, digest); + + return abi.encodePacked(r, s, v); + } } diff --git a/packages/1155-contracts/test/premint/ZoraCreator1155PremintExecutor.t.sol b/packages/1155-contracts/test/premint/ZoraCreator1155PremintExecutor.t.sol index ba4ebcb36..a05418230 100644 --- a/packages/1155-contracts/test/premint/ZoraCreator1155PremintExecutor.t.sol +++ b/packages/1155-contracts/test/premint/ZoraCreator1155PremintExecutor.t.sol @@ -59,7 +59,7 @@ contract ZoraCreator1155PreminterTest is ForkDeploymentConfig, Test { collector = makeAddr("collector"); vm.startPrank(zora); - (, , factoryProxy) = Zora1155FactoryFixtures.setup1155AndFactoryProxy(zora, zora); + (, , factoryProxy, ) = Zora1155FactoryFixtures.setup1155AndFactoryProxy(zora, zora); vm.stopPrank(); factory = ZoraCreator1155FactoryImpl(address(factoryProxy)); @@ -97,34 +97,15 @@ contract ZoraCreator1155PreminterTest is ForkDeploymentConfig, Test { }); } - function makeDefaultV1PremintConfig() private view returns (PremintConfig memory) { - // make a v1 premint config - return - PremintConfig({ - tokenConfig: TokenCreationConfig({ - tokenURI: "blah.token", - maxSupply: 10, - maxTokensPerAddress: 5, - pricePerToken: 0, - mintStart: 0, - mintDuration: 0, - fixedPriceMinter: address(getFixedPriceMinter()), - royaltyRecipient: creator, - royaltyBPS: 10, - royaltyMintSchedule: 0 - }), - uid: 100, - version: 0, - deleted: false - }); - } - function test_v1Signatures_workOnV2Contract() external { // 1. Make contract creation params // configuration of contract to create ContractCreationConfig memory contractConfig = makeDefaultContractCreationConfig(); - PremintConfig memory premintConfig = makeDefaultV1PremintConfig(); + PremintConfig memory premintConfig = Zora1155PremintFixtures.makeDefaultV1PremintConfig({ + fixedPriceMinter: getFixedPriceMinter(), + royaltyRecipient: creator + }); // how many tokens are minted to the executor uint256 quantityToMint = 1;