Skip to content

Commit

Permalink
feat: add create referral to token params
Browse files Browse the repository at this point in the history
  • Loading branch information
kulkarohan authored and oveddan committed Oct 30, 2023
1 parent 7e9f3a3 commit 8885f54
Show file tree
Hide file tree
Showing 14 changed files with 973 additions and 321 deletions.
5 changes: 5 additions & 0 deletions .changeset/violet-starfishes-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@zoralabs/zora-1155-contracts": minor
---

Premint v2 - for add new signature, where createReferral can be specified. ZoraCreator1155PremintExecutor recognizes new version of the signature, and still works with the v1 (legacy) version of the signature. 1155 contract has been updated to now take abi encoded premint config, premint config version, and send it to an external library to decode the config, the signer, and setup actions.
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
102 changes: 95 additions & 7 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,18 +11,90 @@ 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 preminterTypedDataDefinitionV2 = ({
verifyingContract,
premintConfig,
chainId,
}: {
verifyingContract: Address;
premintConfig: PremintConfigV2;
chainId: number;
}) => {
const { tokenConfig, uid, version, deleted } = premintConfig;

const result: TypedDataDefinition<
typeof premintV2Types,
"CreatorAttribution"
> = {
domain: {
chainId,
name: "Preminter",
version: "2",
verifyingContract: verifyingContract,
},
types: premintV2Types,
message: {
tokenConfig,
uid,
version,
deleted,
},
primaryType: "CreatorAttribution",
};

return result;
};

// Convenience method to create the structured typed data
// needed to sign for a premint contract and token
export const preminterTypedDataDefinition = ({
export const preminterTypedDataDefinitionV1 = ({
verifyingContract,
premintConfig,
chainId,
}: {
verifyingContract: Address;
premintConfig: PremintConfig;
premintConfig: PremintConfigV1;
chainId: number;
}) => {
const { tokenConfig, uid, version, deleted } = premintConfig;
Expand Down Expand Up @@ -68,7 +140,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

0 comments on commit 8885f54

Please sign in to comment.