From 038bc741867b9f0c9b96bcbbc3b042cad005ccce Mon Sep 17 00:00:00 2001 From: Dan Oved Date: Wed, 29 Nov 2023 13:51:49 -0800 Subject: [PATCH] fix to work with legacy --- .changeset/rare-wolves-cheat.md | 8 +- .../src/premint/premint-client.test.ts | 12 +- .../src/premint/premint-client.ts | 41 ++--- .../protocol-sdk/src/premint/preminter.ts | 151 +++++++----------- 4 files changed, 91 insertions(+), 121 deletions(-) diff --git a/.changeset/rare-wolves-cheat.md b/.changeset/rare-wolves-cheat.md index 2ec9409a2..824881640 100644 --- a/.changeset/rare-wolves-cheat.md +++ b/.changeset/rare-wolves-cheat.md @@ -2,9 +2,15 @@ "@zoralabs/protocol-sdk": patch --- +### Changes to `preminter` + +* `isValidSignature` now takes either v1 or v2 of a premint config, along with the premint config version. and both recovers the signer address and validates if the signer can create a premint on the given contract. + +### Changes to PremintClient + sdk now supports creating and signing premints for v2 of Premint Config: -* `preminter.isValidSignature` now takes either v1 or v2 of a premint config, along with the premint config version. Underneath the hood, it changes how it works by recovering the signer client-side, then calling an rpc method to check if the signer is authorized to create premings +* `preminter.isValidSignature` * new function `preminter.supportsPremintVersion` which checks if a given token contract supports a given premint config version * new function `preminter.recoverCreatorFromCreatorAttribution` which recovers the creator address from a `CreatorAttribution` event * `preminter.premintTypedDataDefinition` now takes a premint config version, and returns the correct typed data definition for that version diff --git a/packages/protocol-sdk/src/premint/premint-client.test.ts b/packages/protocol-sdk/src/premint/premint-client.test.ts index 61140e793..154fd0fb8 100644 --- a/packages/protocol-sdk/src/premint/premint-client.test.ts +++ b/packages/protocol-sdk/src/premint/premint-client.test.ts @@ -2,6 +2,7 @@ import { foundry } from "viem/chains"; import { describe, expect, vi } from "vitest"; import { createPremintClient } from "./premint-client"; import { anvilTest, forkUrls, makeAnvilTest } from "src/anvil"; +import { PremintSignatureResponse } from "./premint-api-client"; describe("ZoraCreator1155Premint", () => { makeAnvilTest({ @@ -71,7 +72,7 @@ describe("ZoraCreator1155Premint", () => { 20 * 1000, ); - anvilTest.skip( + anvilTest( "can validate premint on network", async ({ viemClients: { publicClient } }) => { const premintClient = createPremintClient({ @@ -79,7 +80,7 @@ describe("ZoraCreator1155Premint", () => { publicClient, }); - const premintData = { + const premintData: PremintSignatureResponse = { collection: { contractAdmin: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", contractName: "Testing Contract", @@ -104,15 +105,12 @@ describe("ZoraCreator1155Premint", () => { "ipfs://bafkreice23maski3x52tsfqgxstx3kbiifnt5jotg3a5ynvve53c4soi2u", }, }, - chain_name: "ZORA-TESTNET", + chain_name: "ZORA-GOERLI", signature: "0x588d19641de9ba1dade4d2bb5387c8dc96f4a990fef69787534b60caead759e6334975a6be10a796da948cd7d1d4f5580b3f84d49d9fa4e0b41c97759507975a1c", } as const; - const signatureValid = await premintClient.isValidSignature({ - // @ts-ignore: Fix enum type - data: premintData, - }); + const signatureValid = await premintClient.isValidSignature(premintData); expect(signatureValid.isValid).toBe(true); }, ); diff --git a/packages/protocol-sdk/src/premint/premint-client.ts b/packages/protocol-sdk/src/premint/premint-client.ts index aeb763bbe..46ed2efeb 100644 --- a/packages/protocol-sdk/src/premint/premint-client.ts +++ b/packages/protocol-sdk/src/premint/premint-client.ts @@ -20,9 +20,10 @@ import { PremintConfigV2, PremintConfigVersion, getPremintCollectionAddress, - isValidSignature, premintTypedDataDefinition, - isAuthorizedToCreateSignatureLegacy, + ContractCreationConfig, + recoverAndValidateSignature, + isAuthorizedToCreatePremint, } from "./preminter"; import type { PremintSignatureGetResponse, @@ -105,7 +106,7 @@ export function getPremintedLogFromReceipt( * @param premint Premint object from the server to convert to one that's compatible with viem * @returns Viem type-compatible premint object */ -export const convertPremint = ( +export const convertPremintV1 = ( premint: PremintSignatureGetResponse["premint"], ) => ({ ...premint, @@ -265,7 +266,7 @@ class PremintClient { uid: uid, }); - const convertedPremint = convertPremint(signatureResponse.premint); + const convertedPremint = convertPremintV1(signatureResponse.premint); const signerData = { ...signatureResponse, premint: { @@ -326,7 +327,7 @@ class PremintClient { ...signatureResponse, collection: convertCollection(signatureResponse.collection), premint: { - ...convertPremint(signatureResponse.premint), + ...convertPremintV1(signatureResponse.premint), deleted: true, }, }; @@ -382,11 +383,13 @@ class PremintClient { }); if (checkSignature) { - const isAuthorized = await isAuthorizedToCreateSignatureLegacy({ + const convertedCollection = convertCollection(collection); + const isAuthorized = await isAuthorizedToCreatePremint({ collection: convertCollection(collection), signature, publicClient: this.publicClient, signer: typeof account === "string" ? account : account.address, + collectionAddress: await this.getCollectionAddres(convertedCollection), ...premintConfigAndVersion, }); if (!isAuthorized) { @@ -514,28 +517,30 @@ class PremintClient { }); } + async getCollectionAddres(collection: ContractCreationConfig) { + return await getPremintCollectionAddress({ + collection, + publicClient: this.publicClient, + }); + } + /** * Check user signature for v1 * * @param data Signature data from the API - * @returns isValid = signature is valid or not, contractAddress = assumed contract address, recoveredSigner = signer from contract + * @returns isValid = signature is valid or not, recoveredSigner = signer from contract */ - async isValidSignature({ - data, - }: { - data: PremintSignatureGetResponse; - }): Promise<{ + async isValidSignature(data: PremintSignatureResponse): Promise<{ isValid: boolean; recoveredSigner: Address | undefined; }> { - const { isAuthorized, recoveredAddress } = await isValidSignature({ - tokenContract: data.collection_address as Address, + const {isAuthorized, recoveredAddress }= await recoverAndValidateSignature({ chainId: this.chain.id, - originalContractAdmin: data.collection.contractAdmin as Address, - premintConfig: convertPremint(data.premint), + signature: data.signature as Hex, + premintConfig: convertPremintV1(data.premint), premintConfigVersion: PremintConfigVersion.V1, + collection: convertCollection(data.collection), publicClient: this.publicClient, - signature: data.signature as Hex, }); return { isValid: isAuthorized, recoveredSigner: recoveredAddress }; @@ -607,7 +612,7 @@ class PremintClient { const numberToMint = BigInt(mintArguments?.quantityToMint || 1); const args = [ convertCollection(data.collection), - convertPremint(data.premint), + convertPremintV1(data.premint), data.signature as Hex, numberToMint, mintArguments?.mintComment || "", diff --git a/packages/protocol-sdk/src/premint/preminter.ts b/packages/protocol-sdk/src/premint/preminter.ts index e80181dda..e32a7bea3 100644 --- a/packages/protocol-sdk/src/premint/preminter.ts +++ b/packages/protocol-sdk/src/premint/preminter.ts @@ -167,55 +167,9 @@ export type IsValidSignatureReturn = { recoveredAddress?: Address; }; -/** - * Checks if the provided signature is valid for the provided premint config. Is valid means that the signature is valid, and that the recovered signer is authorized to - * create premints on the given contract. Works for all versions of the premint config by specifying the premintConfigVersion. - * - * @param params validationProperties - * @param params.tokenContract the address of the 1155 contract - * @param params.originalContractAdmin the original contractAdmin on the ContractCreationConfig for the premint; this is usually the original creator of the premint - * @param params.signature signature to validate - * @param params.chainId the chain id of the current chain - * @param params.publicClient public rpc read-only client - * @param params.premintConfigVersion the version of the premint config - * @param params.premintConfig the premint config - * @returns - */ -export async function isValidSignature({ - tokenContract, - originalContractAdmin, - signature, - chainId, - publicClient, - ...premintConfigAndVersion -}: { - tokenContract: Address; - originalContractAdmin: Address; - signature: Hex; - chainId: number; - publicClient: PublicClient; -} & PremintConfigAndVersion): Promise { - if ( - premintConfigAndVersion.premintConfigVersion === PremintConfigVersion.V1 - ) { - } - const typedData = premintTypedDataDefinition({ - verifyingContract: tokenContract, - chainId, - ...premintConfigAndVersion, - }); - - return await recoverAndValidateSignature({ - typedData, - signature, - publicClient, - premintConfigContractAdmin: originalContractAdmin, - tokenContract: tokenContract, - }); -} - -export async function isAuthorizedToCreateSignatureLegacy({ +export async function isAuthorizedToCreatePremint({ collection, + collectionAddress, publicClient, premintConfig, premintConfigVersion, @@ -223,6 +177,7 @@ export async function isAuthorizedToCreateSignatureLegacy({ signer, }: { collection: ContractCreationConfig; + collectionAddress: Address; publicClient: PublicClient; signature: Hex; signer: Address; @@ -241,36 +196,44 @@ export async function isAuthorizedToCreateSignatureLegacy({ } // otherwize, we must assume the newer version of premint executor is deployed, so we call that. - return isAuthorizedToCreatePremint({ - premintConfigContractAdmin: collection.contractAdmin as Address, - collectionAddress: await getPremintCollectionAddress({ - publicClient: publicClient, - collection, - }), - publicClient, - signer, - }); -} - -export async function isAuthorizedToCreatePremint({ - signer, - collectionAddress, - premintConfigContractAdmin, - publicClient, -}: { - signer: Address; - collectionAddress: Address; - premintConfigContractAdmin: Address; - publicClient: PublicClient; -}) { return await publicClient.readContract({ abi: preminterAbi, address: getPremintExecutorAddress(), functionName: "isAuthorizedToCreatePremint", - args: [signer, premintConfigContractAdmin, collectionAddress], + args: [ + signer, + collection.contractAdmin, + collectionAddress + ], }); } +export async function recoverPremintSigner({ + signature, + ...rest +}: { + signature: Hex; + chainId: number; + verifyingContract: Address; +} & PremintConfigAndVersion): Promise
{ + const typedData = premintTypedDataDefinition(rest); + return await recoverTypedDataAddress({ + ...typedData, + signature, + }); +} + +export async function tryRecoverPremintSigner( + params: Parameters[0], +) { + try { + return await recoverPremintSigner(params); + } catch (error) { + console.error(error); + return undefined; + } +} + /** * Recovers the address from a typed data signature and then checks if the recovered address is authorized to create a premint * @@ -283,41 +246,39 @@ export async function isAuthorizedToCreatePremint({ * @returns */ export async function recoverAndValidateSignature({ - typedData, signature, publicClient, - premintConfigContractAdmin, - tokenContract, + collection, + chainId, + ...premintConfigAndVersion }: { - tokenContract: Address; - premintConfigContractAdmin: Address; - typedData: TypedDataDefinition; + collection: ContractCreationConfig; signature: Hex; + chainId: number; publicClient: PublicClient; -}): Promise { - let recoveredAddress: Address | undefined; - - try { - recoveredAddress = await recoverTypedDataAddress({ - ...typedData, - signature, - }); - } catch (error) { - console.error(error); +} & PremintConfigAndVersion): Promise { + const tokenContract = await getPremintCollectionAddress({ collection, publicClient }); + const recoveredAddress = await tryRecoverPremintSigner({ + ...premintConfigAndVersion, + signature, + verifyingContract: tokenContract, + chainId, + }); + if (!recoverAddress) { return { isAuthorized: false, }; } - let isAuthorized = false; - if (recoveredAddress) - isAuthorized = await isAuthorizedToCreatePremint({ - signer: recoveredAddress, - collectionAddress: tokenContract, - premintConfigContractAdmin, - publicClient, - }); + const isAuthorized = await isAuthorizedToCreatePremint({ + signer: recoveredAddress!, + collection, + collectionAddress: tokenContract, + publicClient, + signature, + ...premintConfigAndVersion + }); return { isAuthorized,