Skip to content

Commit

Permalink
wip on premint v2 sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
oveddan committed Nov 15, 2023
1 parent 888fa53 commit 40ce34f
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 103 deletions.
79 changes: 60 additions & 19 deletions packages/protocol-sdk/src/premint/premint-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ import {
zoraCreatorFixedPriceSaleStrategyAddress,
} from "@zoralabs/protocol-deployments";
import {
PremintConfig,
isValidSignatureV1,
preminterTypedDataDefinition,
PremintConfigAndVersion,
PremintConfigV1,
PremintConfigV2,
PremintConfigVersion,
isValidSignature,
premintTypedDataDefinition,
} from "./preminter";
import type {
PremintSignatureGetResponse,
Expand Down Expand Up @@ -128,10 +131,10 @@ export const convertCollection = (
* @param premint Premint object from viem to convert to a JSON compatible type.
* @returns JSON compatible premint
*/
export const encodePremintForAPI = ({
export const encodePremintV1ForAPI = ({
tokenConfig,
...premint
}: PremintConfig) => ({
}: PremintConfigV1) => ({
...premint,
tokenConfig: {
...tokenConfig,
Expand All @@ -143,6 +146,35 @@ export const encodePremintForAPI = ({
},
});

export const encodePremintV2ForAPI = ({
tokenConfig,
...premint
}: PremintConfigV2) => ({
...premint,
tokenConfig: {
...tokenConfig,
maxSupply: tokenConfig.maxSupply.toString(),
pricePerToken: tokenConfig.pricePerToken.toString(),
mintStart: tokenConfig.mintStart.toString(),
mintDuration: tokenConfig.mintDuration.toString(),
maxTokensPerAddress: tokenConfig.maxTokensPerAddress.toString(),
},
})

export const encodePremintForAPI = ({
premintConfig,
premintConfigVersion
}: PremintConfigAndVersion) => {
if (premintConfigVersion === PremintConfigVersion.V1) {
return encodePremintV1ForAPI(premintConfig);
}
if (premintConfigVersion === PremintConfigVersion.V2) {
return encodePremintV2ForAPI(premintConfig);
}
throw new Error(`Invalid premint config version ${premintConfigVersion}`);
}


/**
* Preminter API to access ZORA Premint functionality.
* Currently only supports V1 premints.
Expand Down Expand Up @@ -252,6 +284,7 @@ class PremintClient extends ClientBase {
contractAdmin: signerData.collection.contractAdmin as Address,
},
premintConfig: signerData.premint,
premintConfigVersion: PremintConfigVersion.V1
});
}

Expand Down Expand Up @@ -306,6 +339,7 @@ class PremintClient extends ClientBase {
uid: uid,
collection: signerData.collection,
premintConfig: signerData.premint,
premintConfigVersion: PremintConfigVersion.V1
});
}

Expand All @@ -319,21 +353,20 @@ class PremintClient extends ClientBase {
walletClient,
publicClient,
verifyingContract,
premintConfig,
uid,
account,
checkSignature,
collection,
...premintConfigAndVersion
}: {
publicClient: PublicClient;
uid: number;
walletClient: WalletClient;
verifyingContract: Address;
checkSignature: boolean;
account?: Address | Account;
premintConfig: PremintConfig;
collection: PremintSignatureGetResponse["collection"];
}) {
} & PremintConfigAndVersion) {
if (!account) {
account = walletClient.account;
}
Expand All @@ -343,28 +376,34 @@ class PremintClient extends ClientBase {

const signature = await walletClient.signTypedData({
account,
...preminterTypedDataDefinition({
...premintTypedDataDefinition({
verifyingContract,
premintConfig,
...premintConfigAndVersion,
chainId: this.chain.id,
}),
});

if (checkSignature) {
const [isValidSignature] = await publicClient.readContract({
abi: zoraCreator1155PremintExecutorImplABI,
address: this.getExecutorAddress(),
functionName: "isValidSignature",
args: [convertCollection(collection), premintConfig, signature],
const { isAuthorized } = await isValidSignature({
chainId: this.chain.id,
contractAddress: verifyingContract,
originalContractAdmin: collection.contractAdmin as Address,
...premintConfigAndVersion,
publicClient: this.getPublicClient(),
signature
});
if (!isValidSignature) {
throw new Error("Invalid signature");
if (!isAuthorized) {
throw new Error("Not authorized to create premint");
}
}

if (premintConfigAndVersion.premintConfigVersion === PremintConfigVersion.V2) {
throw new Error("premint config v2 not supported yet");
}

const apiData = {
collection,
premint: encodePremintForAPI(premintConfig),
premint: encodePremintV1ForAPI(premintConfigAndVersion.premintConfig),
chain_name: this.network.zoraBackendChainName,
signature: signature,
};
Expand Down Expand Up @@ -456,6 +495,7 @@ class PremintClient extends ClientBase {
uid,
verifyingContract: newContractAddress,
premintConfig,
premintConfigVersion: PremintConfigVersion.V1,
checkSignature,
account,
publicClient,
Expand Down Expand Up @@ -503,11 +543,12 @@ class PremintClient extends ClientBase {
}> {
publicClient = this.getPublicClient(publicClient);

const { isAuthorized, recoveredAddress } = await isValidSignatureV1({
const { isAuthorized, recoveredAddress } = await isValidSignature({
contractAddress: data.collection_address as Address,
chainId: this.chain.id,
originalContractAdmin: data.collection.contractAdmin as Address,
premintConfig: convertPremint(data.premint),
premintConfigVersion: PremintConfigVersion.V1,
publicClient: this.getPublicClient(),
signature: data.signature as Hex,
});
Expand Down
75 changes: 55 additions & 20 deletions packages/protocol-sdk/src/premint/preminter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
recoverAddress,
hashDomain,
Address,
zeroAddress,
} from "viem";
import { foundry } from "viem/chains";
import { describe, expect } from "vitest";
Expand All @@ -19,10 +20,13 @@ import {

import {
ContractCreationConfig,
PremintConfig,
TokenCreationConfig,
preminterTypedDataDefinition,
isValidSignatureV1,
PremintConfigV1,
TokenCreationConfigV1,
premintTypedDataDefinition,
isValidSignature,
PremintConfigVersion,
TokenCreationConfigV2,
PremintConfigV2,
} from "./preminter";
import {
AnvilViemClientsTest,
Expand All @@ -42,10 +46,10 @@ const defaultContractConfig = ({
contractName: "My fun NFT",
});

const defaultTokenConfig = (
const defaultTokenConfigV1 = (
fixedPriceMinterAddress: Address,
creatorAccount: Address,
): TokenCreationConfig => ({
): TokenCreationConfigV1 => ({
tokenURI: "ipfs://tokenIpfsId0",
maxSupply: 100n,
maxTokensPerAddress: 10n,
Expand All @@ -58,11 +62,36 @@ const defaultTokenConfig = (
fixedPriceMinter: fixedPriceMinterAddress,
});

const defaultPremintConfig = (
const defaultTokenConfigV2 = (
fixedPriceMinterAddress: Address,
creatorAccount: Address,
createReferral: Address
): TokenCreationConfigV2 => ({
tokenURI: "ipfs://tokenIpfsId0",
maxSupply: 100n,
maxTokensPerAddress: 10n,
pricePerToken: 0n,
mintStart: 0n,
mintDuration: 100n,
royaltyBPS: 200,
payoutRecipient: creatorAccount,
fixedPriceMinter: fixedPriceMinterAddress,
createReferral
});

const defaultPremintConfigV1 = (
fixedPriceMinter: Address,
creatorAccount: Address,
): PremintConfig => ({
tokenConfig: defaultTokenConfig(fixedPriceMinter, creatorAccount),
): PremintConfigV1 => ({
tokenConfig: defaultTokenConfigV1(fixedPriceMinter, creatorAccount),
deleted: false,
uid: 105,
version: 0,
});

const defaultPremintConfigV2 = (
{ fixedPriceMinter, creatorAccount, createReferral = zeroAddress }: { fixedPriceMinter: Address; creatorAccount: Address; createReferral?: Address; }): PremintConfigV2 => ({
tokenConfig: defaultTokenConfigV2(fixedPriceMinter, creatorAccount, createReferral),
deleted: false,
uid: 105,
version: 0,
Expand Down Expand Up @@ -110,7 +139,7 @@ describe("ZoraCreator1155Preminter", () => {
fixedPriceMinterAddress,
accounts: { creatorAccount },
} = await setupContracts({ viemClients });
const premintConfig = defaultPremintConfig(
const premintConfig = defaultPremintConfigV1(
fixedPriceMinterAddress,
creatorAccount,
);
Expand All @@ -128,10 +157,11 @@ describe("ZoraCreator1155Preminter", () => {
});

const signedMessage = await viemClients.walletClient.signTypedData({
...preminterTypedDataDefinition({
...premintTypedDataDefinition({
verifyingContract: contractAddress,
chainId: 999,
premintConfig,
premintConfigVersion: PremintConfigVersion.V1
}),
account: creatorAccount,
});
Expand All @@ -150,14 +180,14 @@ describe("ZoraCreator1155Preminter", () => {
forkUrl: forkUrls.zoraGoerli,
forkBlockNumber: 1676105,
})(
"can sign and recover a signature",
"can sign and recover a v1 premint config signature",
async ({ viemClients }) => {
const {
fixedPriceMinterAddress,
accounts: { creatorAccount },
} = await setupContracts({ viemClients });

const premintConfig = defaultPremintConfig(
const premintConfig = defaultPremintConfigV1(
fixedPriceMinterAddress,
creatorAccount,
);
Expand All @@ -174,21 +204,23 @@ describe("ZoraCreator1155Preminter", () => {

// sign message containing contract and token creation config and uid
const signedMessage = await viemClients.walletClient.signTypedData({
...preminterTypedDataDefinition({
...premintTypedDataDefinition({
verifyingContract: contractAddress,
// we need to sign here for the anvil chain, cause thats where it is run on
chainId: foundry.id,
premintConfig,
premintConfigVersion: PremintConfigVersion.V1
}),
account: creatorAccount,
});

// recover and verify address is correct
const { recoveredAddress, isAuthorized } = await isValidSignatureV1({
const { recoveredAddress, isAuthorized } = await isValidSignature({
contractAddress,
chainId: viemClients.publicClient.chain!.id,
originalContractAdmin: contractConfig.contractAdmin,
premintConfig,
premintConfigVersion: PremintConfigVersion.V1,
publicClient: viemClients.publicClient,
signature: signedMessage,
});
Expand All @@ -209,7 +241,7 @@ describe("ZoraCreator1155Preminter", () => {
accounts: { creatorAccount, collectorAccount },
} = await setupContracts({ viemClients });
// setup contract and token creation parameters
const premintConfig = defaultPremintConfig(
const premintConfig = defaultPremintConfigV1(
fixedPriceMinterAddress,
creatorAccount,
);
Expand All @@ -230,11 +262,12 @@ describe("ZoraCreator1155Preminter", () => {
// have creator sign the message to create the contract
// and the token
const signedMessage = await viemClients.walletClient.signTypedData({
...preminterTypedDataDefinition({
...premintTypedDataDefinition({
verifyingContract: contractAddress,
// we need to sign here for the anvil chain, cause thats where it is run on
chainId: foundry.id,
premintConfig,
premintConfigVersion: PremintConfigVersion.V1
}),
account: creatorAccount,
});
Expand Down Expand Up @@ -326,10 +359,11 @@ describe("ZoraCreator1155Preminter", () => {

// sign the message to create the second token
const signedMessage2 = await viemClients.walletClient.signTypedData({
...preminterTypedDataDefinition({
...premintTypedDataDefinition({
verifyingContract: contractAddress,
chainId: foundry.id,
premintConfig: premintConfig2,
premintConfigVersion: PremintConfigVersion.V1
}),
account: creatorAccount,
});
Expand Down Expand Up @@ -397,7 +431,7 @@ describe("ZoraCreator1155Preminter", () => {
fixedPriceMinterAddress,
accounts: { creatorAccount, collectorAccount },
} = await setupContracts({ viemClients });
const premintConfig = defaultPremintConfig(
const premintConfig = defaultPremintConfigV1(
fixedPriceMinterAddress,
creatorAccount,
);
Expand All @@ -418,11 +452,12 @@ describe("ZoraCreator1155Preminter", () => {
// have creator sign the message to create the contract
// and the token
const signedMessage = await viemClients.walletClient.signTypedData({
...preminterTypedDataDefinition({
...premintTypedDataDefinition({
verifyingContract: contractAddress,
// we need to sign here for the anvil chain, cause thats where it is run on
chainId: foundry.id,
premintConfig,
premintConfigVersion: PremintConfigVersion.V1
}),
account: creatorAccount,
});
Expand Down
Loading

0 comments on commit 40ce34f

Please sign in to comment.