Skip to content

Commit

Permalink
Premintt use encoded bytes in arg
Browse files Browse the repository at this point in the history
  • Loading branch information
oveddan committed Oct 21, 2023
1 parent f9fd6f5 commit 1ac16b6
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 89 deletions.
1 change: 1 addition & 0 deletions packages/1155-contracts/package/preminter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const [
creatorAccount,
collectorAccount,
createReferralAccount,
mintReferral,
] = (await walletClient.getAddresses()) as [
Address,
Address,
Expand Down
56 changes: 26 additions & 30 deletions packages/1155-contracts/package/preminter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,31 +86,6 @@ export const preminterTypedDataDefinitionV2 = ({
return result;
};

const premintV1Types = {
CreatorAttribution: [
{ name: "tokenConfig", type: "TokenCreationConfig" },
// unique id scoped to the contract and token to create.
// ensure that a signature can be replaced, as long as the replacement
// has the same uid, and a newer version.
{ name: "uid", type: "uint32" },
{ name: "version", type: "uint32" },
// if this update should result in the signature being deleted.
{ name: "deleted", type: "bool" },
],
TokenCreationConfig: [
{ name: "tokenURI", type: "string" },
{ name: "maxSupply", type: "uint256" },
{ name: "maxTokensPerAddress", type: "uint64" },
{ name: "pricePerToken", type: "uint96" },
{ name: "mintStart", type: "uint64" },
{ name: "mintDuration", type: "uint64" },
{ name: "royaltyMintSchedule", type: "uint32" },
{ name: "royaltyBPS", type: "uint32" },
{ name: "royaltyRecipient", type: "address" },
{ name: "fixedPriceMinter", type: "address" },
],
};

// Convenience method to create the structured typed data
// needed to sign for a premint contract and token
export const preminterTypedDataDefinitionV1 = ({
Expand All @@ -123,18 +98,39 @@ export const preminterTypedDataDefinitionV1 = ({
chainId: number;
}) => {
const { tokenConfig, uid, version, deleted } = premintConfig;
const types = {
CreatorAttribution: [
{ name: "tokenConfig", type: "TokenCreationConfig" },
// unique id scoped to the contract and token to create.
// ensure that a signature can be replaced, as long as the replacement
// has the same uid, and a newer version.
{ name: "uid", type: "uint32" },
{ name: "version", type: "uint32" },
// if this update should result in the signature being deleted.
{ name: "deleted", type: "bool" },
],
TokenCreationConfig: [
{ name: "tokenURI", type: "string" },
{ name: "maxSupply", type: "uint256" },
{ name: "maxTokensPerAddress", type: "uint64" },
{ name: "pricePerToken", type: "uint96" },
{ name: "mintStart", type: "uint64" },
{ name: "mintDuration", type: "uint64" },
{ name: "royaltyMintSchedule", type: "uint32" },
{ name: "royaltyBPS", type: "uint32" },
{ name: "royaltyRecipient", type: "address" },
{ name: "fixedPriceMinter", type: "address" },
],
};

const result: TypedDataDefinition<
typeof premintV1Types,
"CreatorAttribution"
> = {
const result: TypedDataDefinition<typeof types, "CreatorAttribution"> = {
domain: {
chainId,
name: "Preminter",
version: "1",
verifyingContract: verifyingContract,
},
types: premintV1Types,
types,
message: {
tokenConfig,
uid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,15 +257,10 @@ library ZoraCreator1155Attribution {
library PremintTokenSetup {
uint256 constant PERMISSION_BIT_MINTER = 2 ** 2;

function makeSetupNewTokenCalls(
uint256 newTokenId,
address contractAdmin,
TokenCreationConfigV2 calldata tokenConfig
) internal view returns (bytes[] memory calls) {
function makeSetupNewTokenCalls(uint256 newTokenId, TokenCreationConfigV2 memory tokenConfig) internal view returns (bytes[] memory calls) {
return
_buildCalls({
newTokenId: newTokenId,
contractAdmin: contractAdmin,
fixedPriceMinterAddress: tokenConfig.fixedPriceMinter,
pricePerToken: tokenConfig.pricePerToken,
maxTokensPerAddress: tokenConfig.maxTokensPerAddress,
Expand All @@ -275,15 +270,10 @@ library PremintTokenSetup {
});
}

function makeSetupNewTokenCalls(
uint256 newTokenId,
address contractAdmin,
TokenCreationConfig calldata tokenConfig
) internal view returns (bytes[] memory calls) {
function makeSetupNewTokenCalls(uint256 newTokenId, TokenCreationConfig memory tokenConfig) internal view returns (bytes[] memory calls) {
return
_buildCalls({
newTokenId: newTokenId,
contractAdmin: contractAdmin,
fixedPriceMinterAddress: tokenConfig.fixedPriceMinter,
pricePerToken: tokenConfig.pricePerToken,
maxTokensPerAddress: tokenConfig.maxTokensPerAddress,
Expand All @@ -295,7 +285,6 @@ library PremintTokenSetup {

function _buildCalls(
uint256 newTokenId,
address contractAdmin,
address fixedPriceMinterAddress,
uint96 pricePerToken,
uint64 maxTokensPerAddress,
Expand All @@ -320,7 +309,7 @@ library PremintTokenSetup {
abi.encodeWithSelector(
ZoraCreatorFixedPriceSaleStrategy.setSale.selector,
newTokenId,
_buildNewSalesConfig(contractAdmin, pricePerToken, maxTokensPerAddress, mintDuration)
_buildNewSalesConfig(pricePerToken, maxTokensPerAddress, mintDuration)
)
);

Expand All @@ -333,7 +322,6 @@ library PremintTokenSetup {
}

function _buildNewSalesConfig(
address royaltyRecipient,
uint96 pricePerToken,
uint64 maxTokensPerAddress,
uint64 duration
Expand All @@ -347,7 +335,7 @@ library PremintTokenSetup {
saleStart: saleStart,
saleEnd: saleEnd,
maxTokensPerAddress: maxTokensPerAddress,
fundsRecipient: royaltyRecipient
fundsRecipient: address(0)
});
}
}
Expand All @@ -364,13 +352,49 @@ struct DelegatedTokenSetup {
address createReferral;
}

library PremintEncoding {
function encodePremintV1(PremintConfig memory premintConfig) internal pure returns (bytes memory encodedPremintConfig, bytes32 hashedVersion) {
return (abi.encode(premintConfig), ZoraCreator1155Attribution.HASHED_VERSION_1);
}

function decodePremintV1(bytes memory encodedPremint) internal pure returns (PremintConfig memory) {
return abi.decode(encodedPremint, (PremintConfig));
}

function encodePremintV2(PremintConfigV2 memory premintConfig) internal pure returns (bytes memory encodedPremintConfig, bytes32 hashedVersion) {
return (abi.encode(premintConfig), ZoraCreator1155Attribution.HASHED_VERSION_2);
}

function decodePremintV2(bytes memory encodedPremint) internal pure returns (PremintConfigV2 memory) {
return abi.decode(encodedPremint, (PremintConfigV2));
}
}

library DelegatedTokenCreation {
function recoverDelegatedToken(
PremintConfigV2 calldata premintConfig,
bytes memory premintConfigEncoded,
bytes32 premintVersion,
bytes calldata signature,
address tokenContract,
uint256 nextTokenId
) external view returns (DelegatedTokenSetup memory params, bytes[] memory tokenSetupActions) {
if (premintVersion == ZoraCreator1155Attribution.HASHED_VERSION_1) {
PremintConfig memory premintConfig = PremintEncoding.decodePremintV1(premintConfigEncoded);

return recoverDelegatedToken(premintConfig, signature, tokenContract, nextTokenId);
} else {
PremintConfigV2 memory premintConfig = PremintEncoding.decodePremintV2(premintConfigEncoded);

return recoverDelegatedToken(premintConfig, signature, tokenContract, nextTokenId);
}
}

function recoverDelegatedToken(
PremintConfigV2 memory premintConfig,
bytes calldata signature,
address tokenContract,
uint256 nextTokenId
) private view returns (DelegatedTokenSetup memory params, bytes[] memory tokenSetupActions) {
validatePremint(premintConfig.tokenConfig.mintStart, premintConfig.deleted);

params.structHash = ZoraCreator1155Attribution.hashPremint(premintConfig);
Expand All @@ -390,19 +414,19 @@ library DelegatedTokenCreation {

params.uid = premintConfig.uid;

tokenSetupActions = PremintTokenSetup.makeSetupNewTokenCalls(nextTokenId, params.creator, premintConfig.tokenConfig);
tokenSetupActions = PremintTokenSetup.makeSetupNewTokenCalls({newTokenId: nextTokenId, tokenConfig: premintConfig.tokenConfig});

params.tokenURI = premintConfig.tokenConfig.tokenURI;
params.maxSupply = premintConfig.tokenConfig.maxSupply;
params.createReferral = premintConfig.tokenConfig.createReferral;
}

function recoverDelegatedToken(
PremintConfig calldata premintConfig,
PremintConfig memory premintConfig,
bytes calldata signature,
address tokenContract,
uint256 nextTokenId
) external view returns (DelegatedTokenSetup memory params, bytes[] memory tokenSetupActions) {
) private view returns (DelegatedTokenSetup memory params, bytes[] memory tokenSetupActions) {
validatePremint(premintConfig.tokenConfig.mintStart, premintConfig.deleted);

params.structHash = ZoraCreator1155Attribution.hashPremint(premintConfig);
Expand All @@ -420,7 +444,7 @@ library DelegatedTokenCreation {
params.signature = signature;
params.name = ZoraCreator1155Attribution.NAME;

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

params.tokenURI = premintConfig.tokenConfig.tokenURI;
params.maxSupply = premintConfig.tokenConfig.maxSupply;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {ZoraCreatorFixedPriceSaleStrategy} from "../minters/fixed-price/ZoraCrea
import {IMinter1155} from "../interfaces/IMinter1155.sol";
import {ERC1155DelegationStorageV1} from "../delegation/ERC1155DelegationStorageV1.sol";
import {ZoraCreator1155PremintExecutorImplLib} from "./ZoraCreator1155PremintExecutorImplLib.sol";
import {ZoraCreator1155Attribution, ContractCreationConfig, PremintConfig, PremintConfigV2, TokenCreationConfig, TokenCreationConfigV2} from "./ZoraCreator1155Attribution.sol";
import {PremintEncoding, ZoraCreator1155Attribution, ContractCreationConfig, PremintConfig, PremintConfigV2, TokenCreationConfig, TokenCreationConfigV2} from "./ZoraCreator1155Attribution.sol";

interface IZoraCreator1155PremintV1Signatures {
function delegateSetupNewToken(PremintConfig calldata premintConfig, bytes calldata signature, address sender) external returns (uint256 newTokenId);
Expand Down Expand Up @@ -92,19 +92,23 @@ contract ZoraCreator1155PremintExecutorImpl is
uint256 quantityToMint,
bytes calldata mintArguments
) external payable returns (uint256 newTokenId) {
// get or create the contract with the given params
// contract address is deterministic.
(IZoraCreator1155 tokenContract, bool isNewContract) = ZoraCreator1155PremintExecutorImplLib.getOrCreateContract(zora1155Factory, contractConfig);

// pass the signature and the premint config to the token contract to create the token.
// The token contract will verify the signature and that the signer has permission to create a new token.
// and then create and setup the token using the given token config.
newTokenId = tokenContract.delegateSetupNewToken(premintConfig, signature, msg.sender);

_performMint(tokenContract, premintConfig.tokenConfig.fixedPriceMinter, newTokenId, quantityToMint, mintArguments);

// emit Preminted event
emit PremintedV2(address(tokenContract), newTokenId, isNewContract, premintConfig.uid, msg.sender, quantityToMint, mintArguments);
(bytes memory encodedPremint, bytes32 premintVersion) = PremintEncoding.encodePremintV2(premintConfig);
address fixedPriceMinter = premintConfig.tokenConfig.fixedPriceMinter;
uint32 uid = premintConfig.uid;

// we wrap this here to get around stack too deep issues
{
newTokenId = _premint({
contractConfig: contractConfig,
encodedPremintConfig: encodedPremint,
premintVersion: premintVersion,
signature: signature,
quantityToMint: quantityToMint,
fixedPriceMinter: fixedPriceMinter,
uid: uid,
mintArguments: mintArguments
});
}
}

/// Creates a new token on the given erc1155 contract on behalf of a creator, and mints x tokens to the executor of this transaction.
Expand All @@ -124,17 +128,44 @@ contract ZoraCreator1155PremintExecutorImpl is
uint256 quantityToMint,
bytes memory mintArguments
) public payable returns (uint256 newTokenId) {
(bytes memory encodedPremint, bytes32 premintVersion) = PremintEncoding.encodePremintV1(premintConfig);

return
_premint({
contractConfig: contractConfig,
encodedPremintConfig: encodedPremint,
premintVersion: premintVersion,
signature: signature,
quantityToMint: quantityToMint,
fixedPriceMinter: premintConfig.tokenConfig.fixedPriceMinter,
uid: premintConfig.uid,
mintArguments: mintArguments
});
}

function _premint(
ContractCreationConfig calldata contractConfig,
bytes memory encodedPremintConfig,
bytes32 premintVersion,
bytes calldata signature,
uint256 quantityToMint,
address fixedPriceMinter,
uint32 uid,
bytes memory mintArguments
) private returns (uint256 newTokenId) {
// get or create the contract with the given params
// contract address is deterministic.
(IZoraCreator1155 tokenContract, bool isNewContract) = ZoraCreator1155PremintExecutorImplLib.getOrCreateContract(zora1155Factory, contractConfig);

// assume contract has legacy interface expecting v1 signatures; call it.
newTokenId = IZoraCreator1155PremintV1Signatures(address(tokenContract)).delegateSetupNewToken(premintConfig, signature, msg.sender);
// pass the signature and the premint config to the token contract to create the token.
// The token contract will verify the signature and that the signer has permission to create a new token.
// and then create and setup the token using the given token config.
newTokenId = tokenContract.delegateSetupNewToken(encodedPremintConfig, premintVersion, signature, msg.sender);

_performMint(tokenContract, premintConfig.tokenConfig.fixedPriceMinter, newTokenId, quantityToMint, mintArguments);
_performMint(tokenContract, fixedPriceMinter, newTokenId, quantityToMint, mintArguments);

// emit Preminted event
emit PremintedV2(address(tokenContract), newTokenId, isNewContract, premintConfig.uid, msg.sender, quantityToMint, mintArguments);
emit PremintedV2(address(tokenContract), newTokenId, isNewContract, uid, msg.sender, quantityToMint, mintArguments);
}

function _performMint(
Expand Down
7 changes: 6 additions & 1 deletion packages/1155-contracts/src/interfaces/IZoraCreator1155.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@ interface IZoraCreator1155 is IZoraCreator1155TypesV1, IZoraCreator1155Errors, I
/// @param maxSupply maxSupply for the token, set to 0 for open edition
function setupNewToken(string memory tokenURI, uint256 maxSupply) external returns (uint256 tokenId);

function delegateSetupNewToken(PremintConfigV2 calldata premintConfig, bytes calldata signature, address sender) external returns (uint256 newTokenId);
function delegateSetupNewToken(
bytes memory premintConfigEncoded,
bytes32 premintVersion,
bytes calldata signature,
address sender
) external returns (uint256 newTokenId);

function updateTokenURI(uint256 tokenId, string memory _newURI) external;

Expand Down
19 changes: 3 additions & 16 deletions packages/1155-contracts/src/nft/ZoraCreator1155Impl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -752,27 +752,14 @@ 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(
PremintConfig calldata premintConfig,
bytes calldata signature,
address sender
) external nonReentrant returns (uint256 newTokenId) {
(DelegatedTokenSetup memory params, bytes[] memory tokenSetupActions) = DelegatedTokenCreation.recoverDelegatedToken(
premintConfig,
signature,
address(this),
nextTokenId
);

return _delegateSetupNewToken(params, tokenSetupActions, sender);
}

function delegateSetupNewToken(
PremintConfigV2 calldata premintConfig,
bytes memory premintConfig,
bytes32 premintVersion,
bytes calldata signature,
address sender
) external nonReentrant returns (uint256 newTokenId) {
(DelegatedTokenSetup memory params, bytes[] memory tokenSetupActions) = DelegatedTokenCreation.recoverDelegatedToken(
premintConfig,
premintVersion,
signature,
address(this),
nextTokenId
Expand Down
Loading

0 comments on commit 1ac16b6

Please sign in to comment.