From 34f9be83389eecb13102223dea3f23662c1245a7 Mon Sep 17 00:00:00 2001 From: Dan Oved Date: Mon, 14 Aug 2023 14:35:18 -0700 Subject: [PATCH] Only signing with contract address, we don't need the full contract information to hash Since we now know that the erc1155 is the one that is validating the sig, we can know that the hash is equiv to the contract address --- src/premint/ZoraCreator1155Attribution.sol | 22 +--- .../ZoraCreator1155PremintExecutor.sol | 18 +-- test/premint/ZoraCreator1155Preminter.t.sol | 124 +++++++++--------- 3 files changed, 70 insertions(+), 94 deletions(-) diff --git a/src/premint/ZoraCreator1155Attribution.sol b/src/premint/ZoraCreator1155Attribution.sol index 637ddf0c8..63d8199c3 100644 --- a/src/premint/ZoraCreator1155Attribution.sol +++ b/src/premint/ZoraCreator1155Attribution.sol @@ -40,8 +40,6 @@ struct TokenCreationConfig { } struct PremintConfig { - // The config for the contract to be created - ContractCreationConfig contractConfig; // 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. @@ -121,20 +119,13 @@ library ZoraCreator1155Attribution { bytes32 constant CONTRACT_AND_TOKEN_DOMAIN = keccak256( - "Premint(ContractCreationConfig contractConfig,TokenCreationConfig tokenConfig,uint32 uid,uint32 version,bool deleted)ContractCreationConfig(address contractAdmin,string contractURI,string contractName)TokenCreationConfig(string tokenURI,uint256 maxSupply,uint64 maxTokensPerAddress,uint96 pricePerToken,uint64 mintStart,uint64 mintDuration,uint32 royaltyMintSchedule,uint32 royaltyBPS,address royaltyRecipient)" + "Premint(TokenCreationConfig tokenConfig,uint32 uid,uint32 version,bool deleted)ContractCreationConfig(address contractAdmin,string contractURI,string contractName)TokenCreationConfig(string tokenURI,uint256 maxSupply,uint64 maxTokensPerAddress,uint96 pricePerToken,uint64 mintStart,uint64 mintDuration,uint32 royaltyMintSchedule,uint32 royaltyBPS,address royaltyRecipient)" ); function hashPremint(PremintConfig calldata premintConfig) public pure returns (bytes32) { return keccak256( - abi.encode( - CONTRACT_AND_TOKEN_DOMAIN, - _hashContract(premintConfig.contractConfig), - _hashToken(premintConfig.tokenConfig), - premintConfig.uid, - premintConfig.version, - premintConfig.deleted - ) + abi.encode(CONTRACT_AND_TOKEN_DOMAIN, _hashToken(premintConfig.tokenConfig), premintConfig.uid, premintConfig.version, premintConfig.deleted) ); } @@ -161,15 +152,6 @@ library ZoraCreator1155Attribution { ); } - bytes32 constant CONTRACT_DOMAIN = keccak256("ContractCreationConfig(address contractAdmin,string contractURI,string contractName)"); - - function _hashContract(ContractCreationConfig calldata contractConfig) private pure returns (bytes32) { - return - keccak256( - abi.encode(CONTRACT_DOMAIN, contractConfig.contractAdmin, _stringHash(contractConfig.contractURI), _stringHash(contractConfig.contractName)) - ); - } - function _stringHash(string calldata value) private pure returns (bytes32) { return keccak256(bytes(value)); } diff --git a/src/premint/ZoraCreator1155PremintExecutor.sol b/src/premint/ZoraCreator1155PremintExecutor.sol index 687744421..2fb30fd4e 100644 --- a/src/premint/ZoraCreator1155PremintExecutor.sol +++ b/src/premint/ZoraCreator1155PremintExecutor.sol @@ -47,6 +47,7 @@ contract ZoraCreator1155PremintExecutor { // so it is unaware of order, it just takes the token uri and creates the next token with it // this could include creating the contract. function premint( + ContractCreationConfig calldata contractConfig, PremintConfig calldata premintConfig, bytes calldata signature, uint256 quantityToMint, @@ -61,25 +62,13 @@ contract ZoraCreator1155PremintExecutor { // 6. Future: First minter gets rewards // get or create the contract with the given params - (IZoraCreator1155 tokenContract, bool isNewContract) = _getOrCreateContract(premintConfig.contractConfig); + (IZoraCreator1155 tokenContract, bool isNewContract) = _getOrCreateContract(contractConfig); address contractAddress = address(tokenContract); // have the address setup the new token. The signer must have permission to do this. // (that role enforcement is expected to be in the tokenContract). // the token contract will: - // emit event early to have a cleaner log - emit Preminted( - contractAddress, - newTokenId, - isNewContract, - premintConfig.uid, - premintConfig.contractConfig, - premintConfig.tokenConfig, - msg.sender, - quantityToMint - ); - // * setup the token with the signer as the creator, and follow the creator rewards standard. // * will revert if the token in the contract with the same uid already exists. // * will make sure creator has admin rights to the token. @@ -96,6 +85,9 @@ contract ZoraCreator1155PremintExecutor { quantityToMint, abi.encode(tokenRecipient, mintComment) ); + + // emit Preminted event + emit Preminted(contractAddress, newTokenId, isNewContract, premintConfig.uid, contractConfig, premintConfig.tokenConfig, msg.sender, quantityToMint); } function _getOrCreateContract(ContractCreationConfig calldata contractConfig) private returns (IZoraCreator1155 tokenContract, bool isNewContract) { diff --git a/test/premint/ZoraCreator1155Preminter.t.sol b/test/premint/ZoraCreator1155Preminter.t.sol index ccceb52fa..0692e8d08 100644 --- a/test/premint/ZoraCreator1155Preminter.t.sol +++ b/test/premint/ZoraCreator1155Preminter.t.sol @@ -84,20 +84,14 @@ contract ZoraCreator1155PreminterTest is Test { } function makeDefaultPremintConfig() internal view returns (PremintConfig memory) { - return - PremintConfig({ - contractConfig: makeDefaultContractCreationConfig(), - tokenConfig: makeDefaultTokenCreationConfig(), - uid: 100, - version: 0, - deleted: false - }); + return PremintConfig({tokenConfig: makeDefaultTokenCreationConfig(), uid: 100, version: 0, deleted: false}); } function test_successfullyMintsTokens() external { // 1. Make contract creation params // configuration of contract to create + ContractCreationConfig memory contractConfig = makeDefaultContractCreationConfig(); PremintConfig memory premintConfig = makeDefaultPremintConfig(); // how many tokens are minted to the executor @@ -107,7 +101,7 @@ contract ZoraCreator1155PreminterTest is Test { // get contract hash, which is unique per contract creation config, and can be used // retreive the address created for a contract - address contractAddress = preminter.getContractAddress(premintConfig.contractConfig); + address contractAddress = preminter.getContractAddress(contractConfig); // 2. Call smart contract to get digest to sign for creation params. bytes32 digest = ZoraCreator1155Attribution.premintHashedTypeDataV4(premintConfig, contractAddress, chainId); @@ -120,7 +114,7 @@ contract ZoraCreator1155PreminterTest is Test { address premintExecutor = vm.addr(701); // now call the premint function, using the same config that was used to generate the digest, and the signature vm.prank(premintExecutor); - uint256 tokenId = preminter.premint(premintConfig, signature, quantityToMint, comment); + uint256 tokenId = preminter.premint(contractConfig, premintConfig, signature, quantityToMint, comment); // get the contract address from the preminter based on the contract hash id. IZoraCreator1155 created1155Contract = IZoraCreator1155(contractAddress); @@ -138,7 +132,7 @@ contract ZoraCreator1155PreminterTest is Test { // premint with new token config and signature vm.prank(premintExecutor); - tokenId = preminter.premint(premintConfig, signature, quantityToMint, comment); + tokenId = preminter.premint(contractConfig, premintConfig, signature, quantityToMint, comment); // a new token shoudl have been created, with x tokens minted to the executor, on the same contract address // as before since the contract config didnt change @@ -149,6 +143,7 @@ contract ZoraCreator1155PreminterTest is Test { // 1. Make contract creation params // configuration of contract to create + ContractCreationConfig memory contractConfig = makeDefaultContractCreationConfig(); PremintConfig memory premintConfig = makeDefaultPremintConfig(); // how many tokens are minted to the executor @@ -157,33 +152,35 @@ contract ZoraCreator1155PreminterTest is Test { address premintExecutor = vm.addr(701); string memory comment = "I love it"; - _signAndExecutePremint(premintConfig, creatorPrivateKey, chainId, premintExecutor, quantityToMint, comment); + address contractAddress = preminter.getContractAddress(contractConfig); + _signAndExecutePremint(contractConfig, premintConfig, creatorPrivateKey, chainId, premintExecutor, quantityToMint, comment); // create a sig for another token with same uid, it should revert premintConfig.tokenConfig.tokenURI = "blah2.token"; - bytes memory signature = _signPremint(premintConfig, creatorPrivateKey, chainId); + bytes memory signature = _signPremint(contractAddress, premintConfig, creatorPrivateKey, chainId); vm.startPrank(premintExecutor); // premint with new token config and signature - it should revert vm.expectRevert(abi.encodeWithSelector(ZoraCreator1155Impl.PremintAlreadyExecuted.selector)); - preminter.premint(premintConfig, signature, quantityToMint, comment); + preminter.premint(contractConfig, premintConfig, signature, quantityToMint, comment); // change the version, it should still revert premintConfig.version++; - signature = _signPremint(premintConfig, creatorPrivateKey, chainId); + signature = _signPremint(contractAddress, premintConfig, creatorPrivateKey, chainId); // premint with new token config and signature - it should revert vm.expectRevert(abi.encodeWithSelector(ZoraCreator1155Impl.PremintAlreadyExecuted.selector)); - preminter.premint(premintConfig, signature, quantityToMint, comment); + preminter.premint(contractConfig, premintConfig, signature, quantityToMint, comment); // change the uid, it should not revert premintConfig.uid++; - signature = _signPremint(premintConfig, creatorPrivateKey, chainId); + signature = _signPremint(contractAddress, premintConfig, creatorPrivateKey, chainId); - preminter.premint(premintConfig, signature, quantityToMint, comment); + preminter.premint(contractConfig, premintConfig, signature, quantityToMint, comment); } function test_deleted_preventsTokenFromBeingMinted() external { + ContractCreationConfig memory contractConfig = makeDefaultContractCreationConfig(); PremintConfig memory premintConfig = makeDefaultPremintConfig(); premintConfig.deleted = true; @@ -192,29 +189,35 @@ contract ZoraCreator1155PreminterTest is Test { uint256 quantityToMint = 2; string memory comment = "I love it"; + address contractAddress = preminter.getContractAddress(contractConfig); + // 2. Call smart contract to get digest to sign for creation params. - bytes memory signature = _signPremint(premintConfig, creatorPrivateKey, chainId); + bytes memory signature = _signPremint(contractAddress, premintConfig, creatorPrivateKey, chainId); // now call the premint function, using the same config that was used to generate the digest, and the signature vm.expectRevert(ZoraCreator1155Attribution.PremintDeleted.selector); vm.prank(premintExecutor); - uint256 newTokenId = preminter.premint(premintConfig, signature, quantityToMint, comment); + uint256 newTokenId = preminter.premint(contractConfig, premintConfig, signature, quantityToMint, comment); assertEq(newTokenId, 0, "tokenId"); // make sure no contract was created - assertEq(preminter.getContractAddress(premintConfig.contractConfig).code.length, 0, "contract has been deployed"); + assertEq(contractAddress.code.length, 0, "contract has been deployed"); } function test_emitsPremint_whenNewContract() external { + ContractCreationConfig memory contractConfig = makeDefaultContractCreationConfig(); PremintConfig memory premintConfig = makeDefaultPremintConfig(); + address contractAddress = preminter.getContractAddress(contractConfig); // how many tokens are minted to the executor uint256 quantityToMint = 4; uint256 chainId = block.chainid; // Sign the premint - bytes memory signature = _signPremint(premintConfig, creatorPrivateKey, chainId); + bytes memory signature = _signPremint(contractAddress, premintConfig, creatorPrivateKey, chainId); + + uint256 expectedTokenId = 1; // this account will be used to execute the premint, and should result in a contract being created address premintExecutor = vm.addr(701); @@ -223,29 +226,23 @@ contract ZoraCreator1155PreminterTest is Test { vm.startPrank(premintExecutor); - // we need the contract address to assert the emitted event, so lets premint, get the contract address, rollback, and premint again - uint256 snapshot = vm.snapshot(); - address contractAddress = preminter.getContractAddress(premintConfig.contractConfig); - uint256 tokenId = preminter.premint(premintConfig, signature, quantityToMint, comment); - vm.revertTo(snapshot); - - // now call the premint function, using the same config that was used to generate the digest, and the signature bool createdNewContract = true; vm.expectEmit(true, true, true, true); emit Preminted( contractAddress, - tokenId, + expectedTokenId, createdNewContract, premintConfig.uid, - premintConfig.contractConfig, + contractConfig, premintConfig.tokenConfig, premintExecutor, quantityToMint ); - preminter.premint(premintConfig, signature, quantityToMint, comment); + preminter.premint(contractConfig, premintConfig, signature, quantityToMint, comment); } function test_onlyOwner_hasAdminRights_onCreatedToken() public { + ContractCreationConfig memory contractConfig = makeDefaultContractCreationConfig(); PremintConfig memory premintConfig = makeDefaultPremintConfig(); // how many tokens are minted to the executor @@ -255,14 +252,9 @@ contract ZoraCreator1155PreminterTest is Test { address premintExecutor = vm.addr(701); string memory comment = "I love it"; - (address createdContractAddress, uint256 newTokenId) = _signAndExecutePremint( - premintConfig, - creatorPrivateKey, - chainId, - premintExecutor, - quantityToMint, - comment - ); + address createdContractAddress = preminter.getContractAddress(contractConfig); + + uint256 newTokenId = _signAndExecutePremint(contractConfig, premintConfig, creatorPrivateKey, chainId, premintExecutor, quantityToMint, comment); // get the contract address from the preminter based on the contract hash id. IZoraCreator1155 created1155Contract = IZoraCreator1155(createdContractAddress); @@ -332,14 +324,16 @@ contract ZoraCreator1155PreminterTest is Test { uint32 firstUid = premintConfig.uid; uint32 secondUid = firstUid + 1; - ContractCreationConfig memory firstContractConfig = premintConfig.contractConfig; + ContractCreationConfig memory firstContractConfig = makeDefaultContractCreationConfig(); ContractCreationConfig memory secondContractConfig = ContractCreationConfig( firstContractConfig.contractAdmin, firstContractConfig.contractURI, string.concat(firstContractConfig.contractName, "4") ); - (address firstResultContractAddress, uint256 firstResultTokenId) = _signAndExecutePremint( + address firstContractAddress = preminter.getContractAddress(firstContractConfig); + uint256 firstResultTokenId = _signAndExecutePremint( + firstContractConfig, premintConfig, creatorPrivateKey, chainId, @@ -348,10 +342,11 @@ contract ZoraCreator1155PreminterTest is Test { comment ); - assertEq(IZoraCreator1155(firstResultContractAddress).balanceOf(premintExecutor, firstResultTokenId), quantityToMint); + assertEq(IZoraCreator1155(firstContractAddress).balanceOf(premintExecutor, firstResultTokenId), quantityToMint); premintConfig.uid = secondUid; - (address secondResultContractAddress, uint256 secondResultTokenId) = _signAndExecutePremint( + uint256 secondResultTokenId = _signAndExecutePremint( + firstContractConfig, premintConfig, creatorPrivateKey, chainId, @@ -360,11 +355,11 @@ contract ZoraCreator1155PreminterTest is Test { comment ); - assertEq(firstResultContractAddress, secondResultContractAddress); - assertEq(IZoraCreator1155(firstResultContractAddress).balanceOf(premintExecutor, secondResultTokenId), quantityToMint); + assertEq(IZoraCreator1155(firstContractAddress).balanceOf(premintExecutor, secondResultTokenId), quantityToMint); - premintConfig.contractConfig = secondContractConfig; - (address thirdResultContractAddress, uint256 thirdResultTokenId) = _signAndExecutePremint( + address secondContractAddress = preminter.getContractAddress(secondContractConfig); + uint256 thirdResultTokenId = _signAndExecutePremint( + secondContractConfig, premintConfig, creatorPrivateKey, chainId, @@ -373,8 +368,7 @@ contract ZoraCreator1155PreminterTest is Test { comment ); - assertFalse(firstResultContractAddress == thirdResultContractAddress); - assertEq(IZoraCreator1155(thirdResultContractAddress).balanceOf(premintExecutor, thirdResultTokenId), quantityToMint); + assertEq(IZoraCreator1155(secondContractAddress).balanceOf(premintExecutor, thirdResultTokenId), quantityToMint); } function test_premintCanOnlyBeExecutedAfterStartDate(uint8 startDate, uint8 currentTime) external { @@ -387,6 +381,7 @@ contract ZoraCreator1155PreminterTest is Test { } vm.warp(currentTime); + ContractCreationConfig memory contractConfig = makeDefaultContractCreationConfig(); PremintConfig memory premintConfig = makeDefaultPremintConfig(); premintConfig.tokenConfig.mintStart = startDate; @@ -396,13 +391,13 @@ contract ZoraCreator1155PreminterTest is Test { string memory comment = "I love it"; // get signature for the premint: - bytes memory signature = _signPremint(premintConfig, creatorPrivateKey, chainId); + bytes memory signature = _signPremint(preminter.getContractAddress(contractConfig), premintConfig, creatorPrivateKey, chainId); if (shouldRevert) { vm.expectRevert(ZoraCreator1155Attribution.MintNotYetStarted.selector); } vm.prank(premintExecutor); - preminter.premint(premintConfig, signature, quantityToMint, comment); + preminter.premint(contractConfig, premintConfig, signature, quantityToMint, comment); } function test_premintCanOnlyBeExecutedUpToDurationFromFirstMint(uint8 startDate, uint8 duration, uint8 timeOfFirstMint, uint8 timeOfSecondMint) external { @@ -418,14 +413,17 @@ contract ZoraCreator1155PreminterTest is Test { } // build a premint with a token that has the given start date and duration + ContractCreationConfig memory contractConfig = makeDefaultContractCreationConfig(); PremintConfig memory premintConfig = makeDefaultPremintConfig(); + address contractAddress = preminter.getContractAddress(contractConfig); + premintConfig.tokenConfig.mintStart = startDate; premintConfig.tokenConfig.mintDuration = duration; uint256 chainId = block.chainid; // get signature for the premint: - bytes memory signature = _signPremint(premintConfig, creatorPrivateKey, chainId); + bytes memory signature = _signPremint(contractAddress, premintConfig, creatorPrivateKey, chainId); uint256 quantityToMint = 2; address premintExecutor = vm.addr(701); @@ -434,11 +432,10 @@ contract ZoraCreator1155PreminterTest is Test { vm.startPrank(premintExecutor); vm.warp(timeOfFirstMint); - uint256 tokenId = preminter.premint(premintConfig, signature, quantityToMint, comment); + uint256 tokenId = preminter.premint(contractConfig, premintConfig, signature, quantityToMint, comment); vm.warp(timeOfSecondMint); - address contractAddress = preminter.getContractAddress(premintConfig.contractConfig); // execute mint directly on the contract - and check make sure it reverts if minted after sale start IMinter1155 fixedPriceMinter = factory.defaultMinters()[0]; if (shouldRevert) { @@ -449,23 +446,28 @@ contract ZoraCreator1155PreminterTest is Test { } function _signAndExecutePremint( + ContractCreationConfig memory contractConfig, PremintConfig memory premintConfig, uint256 privateKey, uint256 chainId, address executor, uint256 quantityToMint, string memory comment - ) private returns (address contractAddress, uint256 newTokenId) { - bytes memory signature = _signPremint(premintConfig, privateKey, chainId); + ) private returns (uint256 newTokenId) { + bytes memory signature = _signPremint(preminter.getContractAddress(contractConfig), premintConfig, privateKey, chainId); // now call the premint function, using the same config that was used to generate the digest, and the signature - contractAddress = preminter.getContractAddress(premintConfig.contractConfig); vm.prank(executor); - newTokenId = preminter.premint(premintConfig, signature, quantityToMint, comment); + newTokenId = preminter.premint(contractConfig, premintConfig, signature, quantityToMint, comment); } - function _signPremint(PremintConfig memory premintConfig, uint256 privateKey, uint256 chainId) private view returns (bytes memory) { - bytes32 digest = ZoraCreator1155Attribution.premintHashedTypeDataV4(premintConfig, preminter.getContractAddress(premintConfig.contractConfig), chainId); + function _signPremint( + address contractAddress, + PremintConfig memory premintConfig, + uint256 privateKey, + uint256 chainId + ) private view returns (bytes memory) { + bytes32 digest = ZoraCreator1155Attribution.premintHashedTypeDataV4(premintConfig, contractAddress, chainId); // 3. Sign the digest // create a signature with the digest for the params