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 v1 and v2 sigs - 1155 now takes in encoded premint config and decodes it #284

Closed
2 changes: 1 addition & 1 deletion packages/1155-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"build": "tsup",
"build:sizes": "forge build --sizes",
"bundle-configs": "node script/bundle-chainConfigs.mjs && yarn prettier",
"wagmi": "wagmi generate",
"wagmi": "FOUNDRY_PROFILE=dev forge build && wagmi generate",
"storage-inspect:check": "./script/storage-check.sh check ZoraCreator1155Impl ZoraCreator1155FactoryImpl",
"storage-inspect:generate": "./script/storage-check.sh generate ZoraCreator1155Impl ZoraCreator1155FactoryImpl",
"js-test:watch": "vitest dev",
Expand Down
53 changes: 32 additions & 21 deletions packages/1155-contracts/package/preminter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import {

import {
ContractCreationConfig,
PremintConfig,
TokenCreationConfig,
preminterTypedDataDefinition,
PremintConfigV2,
TokenCreationConfigV2,
encodeMintArguments,
preminterTypedDataDefinitionV2,
} from "./preminter";

const walletClient = createWalletClient({
Expand All @@ -51,8 +52,18 @@ const publicClient = createPublicClient({
type Address = `0x${string}`;

// JSON-RPC Account
const [deployerAccount, creatorAccount, collectorAccount] =
(await walletClient.getAddresses()) as [Address, Address, Address, Address];
const [
deployerAccount,
creatorAccount,
collectorAccount,
createReferralAccount,
] = (await walletClient.getAddresses()) as [
Address,
Address,
Address,
Address,
Address
];

type TestContext = {
preminterAddress: `0x${string}`;
Expand All @@ -75,20 +86,20 @@ const defaultContractConfig = ({

const defaultTokenConfig = (
fixedPriceMinterAddress: Address
): TokenCreationConfig => ({
): TokenCreationConfigV2 => ({
tokenURI: "ipfs://tokenIpfsId0",
maxSupply: 100n,
maxTokensPerAddress: 10n,
pricePerToken: 0n,
mintStart: 0n,
mintDuration: 100n,
royaltyMintSchedule: 30,
royaltyBPS: 200,
royaltyRecipient: creatorAccount,
fixedPriceMinter: fixedPriceMinterAddress,
royaltyBPS: 10,
royaltyRecipient: creatorAccount,
createReferral: createReferralAccount,
});

const defaultPremintConfig = (fixedPriceMinter: Address): PremintConfig => ({
const defaultPremintConfig = (fixedPriceMinter: Address): PremintConfigV2 => ({
tokenConfig: defaultTokenConfig(fixedPriceMinter),
deleted: false,
uid: 105,
Expand Down Expand Up @@ -138,7 +149,7 @@ describe("ZoraCreator1155Preminter", () => {
});

const signedMessage = await walletClient.signTypedData({
...preminterTypedDataDefinition({
...preminterTypedDataDefinitionV2({
verifyingContract: contractAddress,
chainId: 999,
premintConfig,
Expand Down Expand Up @@ -177,7 +188,7 @@ describe("ZoraCreator1155Preminter", () => {

// sign message containing contract and token creation config and uid
const signedMessage = await walletClient.signTypedData({
...preminterTypedDataDefinition({
...preminterTypedDataDefinitionV2({
verifyingContract: contractAddress,
// we need to sign here for the anvil chain, cause thats where it is run on
chainId: anvilChainId,
Expand All @@ -187,11 +198,11 @@ describe("ZoraCreator1155Preminter", () => {
});

// recover and verify address is correct
const recoveredAddress = await publicClient.readContract({
const [, , recoveredAddress] = await publicClient.readContract({
abi: preminterAbi,
address: preminterAddress,
functionName: "recoverSigner",
args: [premintConfig, contractAddress, signedMessage],
functionName: "isValidSignature",
args: [contractConfig, premintConfig, signedMessage],
});

expect(recoveredAddress).to.equal(creatorAccount);
Expand Down Expand Up @@ -226,7 +237,7 @@ describe("ZoraCreator1155Preminter", () => {
// have creator sign the message to create the contract
// and the token
const signedMessage = await walletClient.signTypedData({
...preminterTypedDataDefinition({
...preminterTypedDataDefinitionV2({
verifyingContract: contractAddress,
// we need to sign here for the anvil chain, cause thats where it is run on
chainId: anvilChainId,
Expand Down Expand Up @@ -274,7 +285,7 @@ describe("ZoraCreator1155Preminter", () => {
premintConfig,
signedMessage,
quantityToMint,
comment,
encodeMintArguments({ mintComment: comment }),
],
value: valueToSend,
});
Expand Down Expand Up @@ -319,7 +330,7 @@ describe("ZoraCreator1155Preminter", () => {

// sign the message to create the second token
const signedMessage2 = await walletClient.signTypedData({
...preminterTypedDataDefinition({
...preminterTypedDataDefinitionV2({
verifyingContract: contractAddress,
chainId: foundry.id,
premintConfig: premintConfig2,
Expand All @@ -345,7 +356,7 @@ describe("ZoraCreator1155Preminter", () => {
premintConfig2,
signedMessage2,
quantityToMint2,
comment,
encodeMintArguments({ mintComment: comment }),
],
value: valueToSend2,
});
Expand Down Expand Up @@ -403,7 +414,7 @@ describe("ZoraCreator1155Preminter", () => {
// have creator sign the message to create the contract
// and the token
const signedMessage = await walletClient.signTypedData({
...preminterTypedDataDefinition({
...preminterTypedDataDefinitionV2({
verifyingContract: contractAddress,
// we need to sign here for the anvil chain, cause thats where it is run on
chainId: anvilChainId,
Expand Down Expand Up @@ -439,7 +450,7 @@ describe("ZoraCreator1155Preminter", () => {
premintConfig,
signedMessage,
quantityToMint,
comment,
encodeMintArguments({ mintComment: comment }),
],
value: valueToSend,
});
Expand Down
156 changes: 124 additions & 32 deletions packages/1155-contracts/package/preminter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Address } from "abitype";
import { ExtractAbiFunction, AbiParametersToPrimitiveTypes } from "abitype";
import { zoraCreator1155PremintExecutorImplABI as preminterAbi } from "./wagmiGenerated";
import { TypedDataDefinition } from "viem";
import { TypedDataDefinition, encodeAbiParameters } from "viem";

type PremintInputs = ExtractAbiFunction<
typeof preminterAbi,
Expand All @@ -11,54 +11,130 @@ type PremintInputs = ExtractAbiFunction<
type PreminterHashDataTypes = AbiParametersToPrimitiveTypes<PremintInputs>;

export type ContractCreationConfig = PreminterHashDataTypes[0];
export type PremintConfig = PreminterHashDataTypes[1];
export type TokenCreationConfig = PremintConfig["tokenConfig"];
export type PremintConfigs = PreminterHashDataTypes[1];

export type TokenCreationConfigV1 = PremintConfigV2["tokenConfig"];
export type PremintConfigV2 = Extract<
PremintConfigs,
{
tokenConfig: {
createReferral: string;
};
}
>;
export type PremintConfigV1 = Exclude<PremintConfigs, PremintConfigV2>;
export type TokenCreationConfigV2 = PremintConfigV2["tokenConfig"];

const premintV2Types = {
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: "royaltyBPS", type: "uint32" },
{ name: "royaltyRecipient", type: "address" },
{ name: "fixedPriceMinter", type: "address" },
{ name: "createReferral", type: "address" },
],
};

// Convenience method to create the structured typed data
// needed to sign for a premint contract and token
export const preminterTypedDataDefinition = ({
export const preminterTypedDataDefinitionV2 = ({
verifyingContract,
premintConfig,
chainId,
}: {
verifyingContract: Address;
premintConfig: PremintConfig;
premintConfig: PremintConfigV2;
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 premintV2Types,
"CreatorAttribution"
> = {
domain: {
chainId,
name: "Preminter",
version: "2",
verifyingContract: verifyingContract,
},
types: premintV2Types,
message: {
tokenConfig,
uid,
version,
deleted,
},
primaryType: "CreatorAttribution",
};

const result: TypedDataDefinition<typeof types, "CreatorAttribution"> = {
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 = ({
verifyingContract,
premintConfig,
chainId,
}: {
verifyingContract: Address;
premintConfig: PremintConfigV1;
chainId: number;
}) => {
const { tokenConfig, uid, version, deleted } = premintConfig;

const result: TypedDataDefinition<
typeof premintV1Types,
"CreatorAttribution"
> = {
domain: {
chainId,
name: "Preminter",
version: "1",
verifyingContract: verifyingContract,
},
types,
types: premintV1Types,
message: {
tokenConfig,
uid,
Expand All @@ -68,7 +144,23 @@ export const preminterTypedDataDefinition = ({
primaryType: "CreatorAttribution",
};

// console.log({ result, deleted });

return result;
};

const zeroAddress: Address = "0x0000000000000000000000000000000000000000";

export const encodeMintArguments = ({
mintComment = "",
mintReferral = zeroAddress,
}: {
mintComment?: string;
mintReferral?: Address;
}) => {
return encodeAbiParameters(
[
{ name: "mintReferral", type: "address" },
{ name: "mintComment", type: "string" },
],
[mintReferral, mintComment]
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ contract DeployProxiesToNewChain is ZoraDeployerBase, DeploymentTestingUtils {

console2.log("testing premint");

signAndExecutePremint(deployment.preminterProxy);
signAndExecutePremint(deployment.preminterProxy, vm.envAddress("TEST_PREMINT_FUNDS_RECIPIENT"));

vm.stopBroadcast();

Expand Down
Loading
Loading