diff --git a/src/interfaces/IZoraCreator1155.sol b/src/interfaces/IZoraCreator1155.sol index d8b79e2b9..d396a385e 100644 --- a/src/interfaces/IZoraCreator1155.sol +++ b/src/interfaces/IZoraCreator1155.sol @@ -60,6 +60,7 @@ interface IZoraCreator1155 is IZoraCreator1155TypesV1, IVersionedContract, IOwna 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); error TokenIdMismatch(uint256 expected, uint256 actual); error UserMissingRoleForToken(address user, uint256 tokenId, uint256 role); diff --git a/src/nft/ZoraCreator1155Impl.sol b/src/nft/ZoraCreator1155Impl.sol index e554f6641..b89a6514c 100644 --- a/src/nft/ZoraCreator1155Impl.sol +++ b/src/nft/ZoraCreator1155Impl.sol @@ -727,10 +727,6 @@ contract ZoraCreator1155Impl is /* start eip712 functionality */ mapping(uint32 => uint256) public delegatedTokenId; - event CreatorAttribution(bytes32 structHash, bytes32 domainName, bytes32 version, bytes signature); - - error PremintAlreadyExecuted(); - function delegateSetupNewToken(PremintConfig calldata premintConfig, bytes calldata signature) public nonReentrant returns (uint256 newTokenId) { // if a token has already been created for a premint config with this uid: if (delegatedTokenId[premintConfig.uid] != 0) { @@ -740,14 +736,14 @@ contract ZoraCreator1155Impl is bytes32 hashedPremintConfig = ZoraCreator1155Attribution.validateAndHashPremint(premintConfig); - // this is what attributes this token to have been created by the original creator - emit CreatorAttribution(hashedPremintConfig, ZoraCreator1155Attribution.HASHED_NAME, ZoraCreator1155Attribution.HASHED_VERSION, signature); - // recover the signer from the data - address recoveredSigner = ZoraCreator1155Attribution.recoverSignerHashed(hashedPremintConfig, signature, address(this), block.chainid); + address creator = ZoraCreator1155Attribution.recoverSignerHashed(hashedPremintConfig, signature, address(this), block.chainid); + + // this is what attributes this token to have been created by the original creator + emit CreatorAttribution(hashedPremintConfig, ZoraCreator1155Attribution.NAME, ZoraCreator1155Attribution.VERSION, creator, signature); // require that the signer can create new tokens (is a valid creator) - _requireAdminOrRole(recoveredSigner, CONTRACT_BASE_ID, PERMISSION_BIT_MINTER); + _requireAdminOrRole(creator, CONTRACT_BASE_ID, PERMISSION_BIT_MINTER); // create the new token; msg sender will have PERMISSION_BIT_ADMIN on the new token newTokenId = _setupNewTokenAndPermission(premintConfig.tokenConfig.tokenURI, premintConfig.tokenConfig.maxSupply, msg.sender, PERMISSION_BIT_ADMIN); @@ -755,7 +751,7 @@ contract ZoraCreator1155Impl is delegatedTokenId[premintConfig.uid] = newTokenId; // invoke setup actions for new token, to save contract size, first get them from an external lib - bytes[] memory tokenSetupActions = PremintTokenSetup.makeSetupNewTokenCalls(newTokenId, recoveredSigner, premintConfig.tokenConfig); + bytes[] memory tokenSetupActions = PremintTokenSetup.makeSetupNewTokenCalls(newTokenId, creator, premintConfig.tokenConfig); // then invoke them, calling account should be original msg.sender, which has admin on the new token _multicallInternal(tokenSetupActions); @@ -764,6 +760,6 @@ contract ZoraCreator1155Impl is _removePermission(newTokenId, msg.sender, PERMISSION_BIT_ADMIN); // grant the token creator as admin of the newly created token - _addPermission(newTokenId, recoveredSigner, PERMISSION_BIT_ADMIN); + _addPermission(newTokenId, creator, PERMISSION_BIT_ADMIN); } } diff --git a/src/premint/ZoraCreator1155Attribution.sol b/src/premint/ZoraCreator1155Attribution.sol index 5efab4f8b..cc3d4bcf7 100644 --- a/src/premint/ZoraCreator1155Attribution.sol +++ b/src/premint/ZoraCreator1155Attribution.sol @@ -58,9 +58,11 @@ struct PremintConfig { /// @author @oveddan library ZoraCreator1155Attribution { /* start eip712 functionality */ - bytes32 public constant HASHED_NAME = keccak256(bytes("Preminter")); - bytes32 public constant HASHED_VERSION = keccak256(bytes("1")); - bytes32 public constant TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); + 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. diff --git a/test/premint/ZoraCreator1155Preminter.t.sol b/test/premint/ZoraCreator1155Preminter.t.sol index e194e3ee8..6d6868b06 100644 --- a/test/premint/ZoraCreator1155Preminter.t.sol +++ b/test/premint/ZoraCreator1155Preminter.t.sol @@ -156,6 +156,36 @@ contract ZoraCreator1155PreminterTest is ForkDeploymentConfig, Test { assertEq(created1155Contract.balanceOf(premintExecutor, tokenId), quantityToMint); } + event CreatorAttribution(bytes32 structHash, string domainName, string version, address creator, bytes signature); + + function test_premint_emitsCreatorAttribution_fromErc1155Contract() external { + // build a premint + ContractCreationConfig memory contractConfig = makeDefaultContractCreationConfig(); + PremintConfig memory premintConfig = makeDefaultPremintConfig(); + + // sign and execute premint + uint256 chainId = block.chainid; + + address deterministicAddress = preminter.getContractAddress(contractConfig); + bytes32 structHash = ZoraCreator1155Attribution.premintHashedTypeDataV4(premintConfig, deterministicAddress, chainId); + bytes memory signature = _sign(creatorPrivateKey, structHash); + + uint256 quantityToMint = 4; + string memory comment = "hi"; + uint256 mintCost = mintFeeAmount * quantityToMint; + // this account will be used to execute the premint, and should result in a contract being created + vm.deal(collector, mintCost); + + vm.prank(collector); + + // verify CreatorAttribution was emitted from the erc1155 contract + vm.expectEmit(true, false, false, false, deterministicAddress); + emit CreatorAttribution(structHash, ZoraCreator1155Attribution.NAME, ZoraCreator1155Attribution.VERSION, creator, signature); + + // create contract and token via premint + preminter.premint{value: mintCost}(contractConfig, premintConfig, signature, quantityToMint, comment); + } + /// @notice gets the chains to do fork tests on, by reading environment var FORK_TEST_CHAINS. /// Chains are by name, and must match whats under `rpc_endpoints` in the foundry.toml function getForkTestChains() private view returns (string[] memory result) {