Skip to content

Commit

Permalink
Premint: add creator to creator attribution event (#163)
Browse files Browse the repository at this point in the history
* Creator attribution - add creator to event

* * Renamed creator attribution args from bytes32 to string to match the standard
* Added test that verifies creator attribution event emitted
  • Loading branch information
oveddan committed Sep 18, 2023
1 parent 5aa51ff commit 28749cd
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/interfaces/IZoraCreator1155.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 7 additions & 11 deletions src/nft/ZoraCreator1155Impl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -740,22 +736,22 @@ 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);

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);
Expand All @@ -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);
}
}
8 changes: 5 additions & 3 deletions src/premint/ZoraCreator1155Attribution.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
30 changes: 30 additions & 0 deletions test/premint/ZoraCreator1155Preminter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 28749cd

Please sign in to comment.