Skip to content

Commit

Permalink
fix to work with legacy
Browse files Browse the repository at this point in the history
  • Loading branch information
oveddan committed Nov 29, 2023
1 parent 833f1ba commit 038bc74
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 121 deletions.
8 changes: 7 additions & 1 deletion .changeset/rare-wolves-cheat.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 5 additions & 7 deletions packages/protocol-sdk/src/premint/premint-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -71,15 +72,15 @@ describe("ZoraCreator1155Premint", () => {
20 * 1000,
);

anvilTest.skip(
anvilTest(
"can validate premint on network",
async ({ viemClients: { publicClient } }) => {
const premintClient = createPremintClient({
chain: foundry,
publicClient,
});

const premintData = {
const premintData: PremintSignatureResponse = {
collection: {
contractAdmin: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
contractName: "Testing Contract",
Expand All @@ -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);
},
);
Expand Down
41 changes: 23 additions & 18 deletions packages/protocol-sdk/src/premint/premint-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import {
PremintConfigV2,
PremintConfigVersion,
getPremintCollectionAddress,
isValidSignature,
premintTypedDataDefinition,
isAuthorizedToCreateSignatureLegacy,
ContractCreationConfig,
recoverAndValidateSignature,
isAuthorizedToCreatePremint,
} from "./preminter";
import type {
PremintSignatureGetResponse,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -265,7 +266,7 @@ class PremintClient {
uid: uid,
});

const convertedPremint = convertPremint(signatureResponse.premint);
const convertedPremint = convertPremintV1(signatureResponse.premint);
const signerData = {
...signatureResponse,
premint: {
Expand Down Expand Up @@ -326,7 +327,7 @@ class PremintClient {
...signatureResponse,
collection: convertCollection(signatureResponse.collection),
premint: {
...convertPremint(signatureResponse.premint),
...convertPremintV1(signatureResponse.premint),
deleted: true,
},
};
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 };
Expand Down Expand Up @@ -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 || "",
Expand Down
151 changes: 56 additions & 95 deletions packages/protocol-sdk/src/premint/preminter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,62 +167,17 @@ 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<IsValidSignatureReturn> {
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,
signature,
signer,
}: {
collection: ContractCreationConfig;
collectionAddress: Address;
publicClient: PublicClient;
signature: Hex;
signer: Address;
Expand All @@ -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<Address> {
const typedData = premintTypedDataDefinition(rest);
return await recoverTypedDataAddress({
...typedData,
signature,
});
}

export async function tryRecoverPremintSigner(
params: Parameters<typeof recoverPremintSigner>[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
*
Expand All @@ -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<IsValidSignatureReturn> {
let recoveredAddress: Address | undefined;

try {
recoveredAddress = await recoverTypedDataAddress({
...typedData,
signature,
});
} catch (error) {
console.error(error);
} & PremintConfigAndVersion): Promise<IsValidSignatureReturn> {
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,
Expand Down

0 comments on commit 038bc74

Please sign in to comment.