Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Premint: add creator to creator attribution event #163

Merged
merged 2 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
kulkarohan marked this conversation as resolved.
Show resolved Hide resolved

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