Skip to content

Commit

Permalink
support v1 and v2 sigs
Browse files Browse the repository at this point in the history
  • Loading branch information
oveddan committed Oct 18, 2023
1 parent 2288400 commit 8cb1d90
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 36 deletions.
131 changes: 120 additions & 11 deletions packages/1155-contracts/src/delegation/ZoraCreator1155Attribution.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity 0.8.17;

import {IMinter1155} from "../interfaces/IMinter1155.sol";
import {IZoraCreator1155} from "../interfaces/IZoraCreator1155.sol";
import {IZoraCreator1155Errors} from "../interfaces/IZoraCreator1155Errors.sol";
import {ICreatorRoyaltiesControl} from "../interfaces/ICreatorRoyaltiesControl.sol";
import {ECDSAUpgradeable} from "@zoralabs/openzeppelin-contracts-upgradeable/contracts/utils/cryptography/ECDSAUpgradeable.sol";
import {ZoraCreatorFixedPriceSaleStrategy} from "../minters/fixed-price/ZoraCreatorFixedPriceSaleStrategy.sol";
Expand Down Expand Up @@ -104,14 +105,14 @@ library ZoraCreator1155Attribution {
"CreatorAttribution(TokenCreationConfig tokenConfig,uint32 uid,uint32 version,bool deleted)TokenCreationConfig(string tokenURI,uint256 maxSupply,uint64 maxTokensPerAddress,uint96 pricePerToken,uint64 mintStart,uint64 mintDuration,uint32 royaltyBPS,address royaltyRecipient,address fixedPriceMinter,address createReferral)"
);

function hashPremint(PremintConfig calldata premintConfig) public pure returns (bytes32) {
function hashPremint(PremintConfig calldata premintConfig) internal pure returns (bytes32) {
return
keccak256(
abi.encode(ATTRIBUTION_DOMAIN_V1, _hashToken(premintConfig.tokenConfig), premintConfig.uid, premintConfig.version, premintConfig.deleted)
);
}

function hashPremint(PremintConfigV2 calldata premintConfig) public pure returns (bytes32) {
function hashPremint(PremintConfigV2 calldata premintConfig) external pure returns (bytes32) {
return
keccak256(
abi.encode(ATTRIBUTION_DOMAIN_V2, _hashToken(premintConfig.tokenConfig), premintConfig.uid, premintConfig.version, premintConfig.deleted)
Expand Down Expand Up @@ -261,9 +262,49 @@ library PremintTokenSetup {
address contractAdmin,
TokenCreationConfigV2 calldata tokenConfig
) external view returns (bytes[] memory calls) {
return
buildCalls({
newTokenId: newTokenId,
contractAdmin: contractAdmin,
fixedPriceMinterAddress: tokenConfig.fixedPriceMinter,
pricePerToken: tokenConfig.pricePerToken,
maxTokensPerAddress: tokenConfig.maxTokensPerAddress,
mintDuration: tokenConfig.mintDuration,
royaltyBPS: tokenConfig.royaltyBPS,
royaltyRecipient: tokenConfig.royaltyRecipient
});
}

function makeSetupNewTokenCalls(
uint256 newTokenId,
address contractAdmin,
TokenCreationConfig calldata tokenConfig
) external view returns (bytes[] memory calls) {
return
buildCalls({
newTokenId: newTokenId,
contractAdmin: contractAdmin,
fixedPriceMinterAddress: tokenConfig.fixedPriceMinter,
pricePerToken: tokenConfig.pricePerToken,
maxTokensPerAddress: tokenConfig.maxTokensPerAddress,
mintDuration: tokenConfig.mintDuration,
royaltyBPS: tokenConfig.royaltyBPS,
royaltyRecipient: tokenConfig.royaltyRecipient
});
}

function buildCalls(
uint256 newTokenId,
address contractAdmin,
address fixedPriceMinterAddress,
uint96 pricePerToken,
uint64 maxTokensPerAddress,
uint64 mintDuration,
uint32 royaltyBPS,
address royaltyRecipient
) internal view returns (bytes[] memory calls) {
calls = new bytes[](3);

address fixedPriceMinterAddress = tokenConfig.fixedPriceMinter;
// build array of the calls to make
// get setup actions and invoke them
// set up the sales strategy
Expand All @@ -279,24 +320,20 @@ library PremintTokenSetup {
abi.encodeWithSelector(
ZoraCreatorFixedPriceSaleStrategy.setSale.selector,
newTokenId,
_buildNewSalesConfig(contractAdmin, tokenConfig.pricePerToken, tokenConfig.maxTokensPerAddress, tokenConfig.mintDuration)
_buildNewSalesConfig(contractAdmin, pricePerToken, maxTokensPerAddress, mintDuration)
)
);

// set the royalty config on that token:
calls[2] = abi.encodeWithSelector(
IZoraCreator1155.updateRoyaltiesForToken.selector,
newTokenId,
ICreatorRoyaltiesControl.RoyaltyConfiguration({
royaltyBPS: tokenConfig.royaltyBPS,
royaltyRecipient: tokenConfig.royaltyRecipient,
royaltyMintSchedule: 0
})
ICreatorRoyaltiesControl.RoyaltyConfiguration({royaltyBPS: royaltyBPS, royaltyRecipient: royaltyRecipient, royaltyMintSchedule: 0})
);
}

function _buildNewSalesConfig(
address creator,
address royaltyRecipient,
uint96 pricePerToken,
uint64 maxTokensPerAddress,
uint64 duration
Expand All @@ -310,7 +347,79 @@ library PremintTokenSetup {
saleStart: saleStart,
saleEnd: saleEnd,
maxTokensPerAddress: maxTokensPerAddress,
fundsRecipient: creator
fundsRecipient: royaltyRecipient
});
}
}

struct CreatorAttributionParams {
bytes32 structHash;
string name;
string version;
address creator;
bytes signature;
}

library DelegatedTokenCreation {
function recoverDelegatedToken(
PremintConfigV2 calldata premintConfig,
bytes calldata signature,
address tokenContract,
uint256 nextTokenId
) external returns (CreatorAttributionParams memory creatorAttribution, bytes[] memory tokenSetupActions) {
validatePremint(premintConfig);

creatorAttribution.structHash = ZoraCreator1155Attribution.hashPremint(premintConfig);

creatorAttribution.version = ZoraCreator1155Attribution.VERSION_2;

creatorAttribution.creator = ZoraCreator1155Attribution.recoverSignerHashed(
creatorAttribution.structHash,
signature,
tokenContract,
ZoraCreator1155Attribution.HASHED_VERSION_2,
block.chainid
);

creatorAttribution.signature = signature;
creatorAttribution.name = ZoraCreator1155Attribution.NAME;

tokenSetupActions = PremintTokenSetup.makeSetupNewTokenCalls(nextTokenId, creatorAttribution.creator, premintConfig.tokenConfig);
}

function recoverDelegatedToken(
PremintConfig calldata premintConfig,
bytes calldata signature,
address tokenContract,
uint256 nextTokenId
) external returns (CreatorAttributionParams memory creatorAttribution, bytes[] memory tokenSetupActions) {
creatorAttribution.structHash = ZoraCreator1155Attribution.hashPremint(premintConfig);

creatorAttribution.version = ZoraCreator1155Attribution.VERSION_1;

creatorAttribution.creator = ZoraCreator1155Attribution.recoverSignerHashed(
creatorAttribution.structHash,
signature,
tokenContract,
ZoraCreator1155Attribution.HASHED_VERSION_1,
block.chainid
);

creatorAttribution.signature = signature;
creatorAttribution.name = ZoraCreator1155Attribution.NAME;

tokenSetupActions = PremintTokenSetup.makeSetupNewTokenCalls(nextTokenId, creatorAttribution.creator, premintConfig.tokenConfig);
}

function validatePremint(PremintConfigV2 calldata premintConfig) private view {
if (premintConfig.tokenConfig.mintStart != 0 && premintConfig.tokenConfig.mintStart > block.timestamp) {
// if the mint start is in the future, then revert
revert IZoraCreator1155Errors.MintNotYetStarted();
}
if (premintConfig.deleted) {
// if the signature says to be deleted, then dont execute any further minting logic;
// return 0
revert IZoraCreator1155Errors.PremintDeleted();
}
}
}
90 changes: 65 additions & 25 deletions packages/1155-contracts/src/nft/ZoraCreator1155Impl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +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 {ZoraCreator1155Attribution, PremintTokenSetup, PremintConfigV2} from "../delegation/ZoraCreator1155Attribution.sol";
import {ZoraCreator1155Attribution, PremintTokenSetup, PremintConfig, PremintConfigV2, DelegatedTokenCreation, CreatorAttributionParams} from "../delegation/ZoraCreator1155Attribution.sol";

/// Imagine. Mint. Enjoy.
/// @title ZoraCreator1155Impl
Expand Down Expand Up @@ -739,55 +739,95 @@ contract ZoraCreator1155Impl is
/// @param premintConfig configuration of token to be created
/// @param signature EIP-712 Signature created on the premintConfig by an account with the PERMISSION_BIT_MINTER role on the contract.
function delegateSetupNewToken(
PremintConfigV2 calldata premintConfig,
PremintConfig calldata premintConfig,
bytes calldata signature,
address sender
) public nonReentrant returns (uint256 newTokenId) {
// if a token has already been created for a premint config with this uid:
if (delegatedTokenId[premintConfig.uid] != 0) {
// return its token id
return delegatedTokenId[premintConfig.uid];
}

validatePremint(premintConfig);
) external nonReentrant returns (uint256 newTokenId) {
(CreatorAttributionParams memory creatorAttribution, bytes[] memory tokenSetupActions) = DelegatedTokenCreation.recoverDelegatedToken(
premintConfig,
signature,
address(this),
nextTokenId
);

bytes32 hashedPremintConfig = ZoraCreator1155Attribution.hashPremint(premintConfig);
return _delegateSetupNewToken(
premintConfig.uid,
creatorAttribution,
tokenSetupActions,
premintConfig.tokenConfig.tokenURI,
premintConfig.tokenConfig.maxSupply,
address(0),
sender
);
}

// recover the signer from the data
address creator = ZoraCreator1155Attribution.recoverSignerHashed(
hashedPremintConfig,
function delegateSetupNewToken(
PremintConfigV2 calldata premintConfig,
bytes calldata signature,
address sender
) external nonReentrant returns (uint256 newTokenId) {
(CreatorAttributionParams memory creatorAttribution, bytes[] memory tokenSetupActions) = DelegatedTokenCreation.recoverDelegatedToken(
premintConfig,
signature,
address(this),
ZoraCreator1155Attribution.HASHED_VERSION_2,
block.chainid
nextTokenId
);

return _delegateSetupNewToken(
premintConfig.uid,
creatorAttribution,
tokenSetupActions,
premintConfig.tokenConfig.tokenURI,
premintConfig.tokenConfig.maxSupply,
premintConfig.tokenConfig.createReferral,
sender
);
}

function _delegateSetupNewToken(
uint32 uid,
CreatorAttributionParams memory creatorAttribution,
bytes[] memory tokenSetupActions,
string calldata tokenURI,
uint256 maxSupply,
address createReferral,
address sender
) internal returns (uint256 newTokenId) {
// if a token has already been created for a premint config with this uid:
if (delegatedTokenId[uid] != 0) {
// return its token id
return delegatedTokenId[uid];
}

// this is what attributes this token to have been created by the original creator
emit CreatorAttribution(hashedPremintConfig, ZoraCreator1155Attribution.NAME, ZoraCreator1155Attribution.VERSION_2, creator, signature);
emit CreatorAttribution(
creatorAttribution.structHash,
creatorAttribution.name,
creatorAttribution.version,
creatorAttribution.creator,
creatorAttribution.signature
);

// require that the signer can create new tokens (is a valid creator)
_requireAdminOrRole(creator, CONTRACT_BASE_ID, PERMISSION_BIT_MINTER);
_requireAdminOrRole(creatorAttribution.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);
newTokenId = _setupNewTokenAndPermission(tokenURI, maxSupply, msg.sender, PERMISSION_BIT_ADMIN);

_setCreateReferral(newTokenId, premintConfig.tokenConfig.createReferral);
_setCreateReferral(newTokenId, createReferral);

delegatedTokenId[premintConfig.uid] = newTokenId;
delegatedTokenId[uid] = newTokenId;

firstMinters[newTokenId] = sender;

// invoke setup actions for new token, to save contract size, first get them from an external lib
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);

// remove the token creator as admin of the newly created token:
_removePermission(newTokenId, msg.sender, PERMISSION_BIT_ADMIN);

// grant the token creator as admin of the newly created token
_addPermission(newTokenId, creator, PERMISSION_BIT_ADMIN);
_addPermission(newTokenId, creatorAttribution.creator, PERMISSION_BIT_ADMIN);
}

function validatePremint(PremintConfigV2 calldata premintConfig) private view {
Expand Down

0 comments on commit 8cb1d90

Please sign in to comment.