From ef22d73601694025a586b15386f3cf2587aa19b9 Mon Sep 17 00:00:00 2001 From: iain nash Date: Fri, 3 Nov 2023 01:49:46 -0400 Subject: [PATCH 1/3] Adding automated anvil testing client creation. --- packages/protocol-sdk/.env.anvil | 2 - packages/protocol-sdk/package.json | 7 +- packages/protocol-sdk/src/anvil.ts | 83 +++ .../src/{ => apis}/client-base.ts | 2 +- .../generated/discover-api-types.ts | 0 .../{ => apis}/generated/premint-api-types.ts | 0 .../src/{ => apis}/http-api-base.ts | 0 .../1155-create-helper.test.ts} | 4 +- .../src/{ => create}/1155-create-helper.ts | 2 +- packages/protocol-sdk/src/index.ts | 10 +- .../src/{ => mint}/mint-api-client.ts | 8 +- .../src/{ => mint}/mint-client.test.ts | 59 +- .../src/{ => mint}/mint-client.ts | 2 +- .../src/{ => premint}/premint-api-client.ts | 6 +- .../src/{ => premint}/premint-client.test.ts | 158 ++---- .../src/{ => premint}/premint-client.ts | 4 +- .../src/premint/preminter.test.ts | 522 ++++++++++++++++++ .../src/{ => premint}/preminter.ts | 0 packages/protocol-sdk/src/preminter.test.ts | 510 ----------------- packages/protocol-sdk/tsconfig.json | 2 +- 20 files changed, 704 insertions(+), 677 deletions(-) delete mode 100644 packages/protocol-sdk/.env.anvil create mode 100644 packages/protocol-sdk/src/anvil.ts rename packages/protocol-sdk/src/{ => apis}/client-base.ts (98%) rename packages/protocol-sdk/src/{ => apis}/generated/discover-api-types.ts (100%) rename packages/protocol-sdk/src/{ => apis}/generated/premint-api-types.ts (100%) rename packages/protocol-sdk/src/{ => apis}/http-api-base.ts (100%) rename packages/protocol-sdk/src/{create-helper.test.ts => create/1155-create-helper.test.ts} (99%) rename packages/protocol-sdk/src/{ => create}/1155-create-helper.ts (99%) rename packages/protocol-sdk/src/{ => mint}/mint-api-client.ts (87%) rename packages/protocol-sdk/src/{ => mint}/mint-client.test.ts (77%) rename packages/protocol-sdk/src/{ => mint}/mint-client.ts (99%) rename packages/protocol-sdk/src/{ => premint}/premint-api-client.ts (91%) rename packages/protocol-sdk/src/{ => premint}/premint-client.test.ts (63%) rename packages/protocol-sdk/src/{ => premint}/premint-client.ts (99%) create mode 100644 packages/protocol-sdk/src/premint/preminter.test.ts rename packages/protocol-sdk/src/{ => premint}/preminter.ts (100%) delete mode 100644 packages/protocol-sdk/src/preminter.test.ts diff --git a/packages/protocol-sdk/.env.anvil b/packages/protocol-sdk/.env.anvil deleted file mode 100644 index 6430e7bc5..000000000 --- a/packages/protocol-sdk/.env.anvil +++ /dev/null @@ -1,2 +0,0 @@ -FORK_RPC_URL="https://rpc.zora.co/" -FORK_BLOCK_NUMBER=6133407 \ No newline at end of file diff --git a/packages/protocol-sdk/package.json b/packages/protocol-sdk/package.json index b137144fb..402c07f01 100644 --- a/packages/protocol-sdk/package.json +++ b/packages/protocol-sdk/package.json @@ -1,6 +1,6 @@ { - "name": "@zoralabs/premint-sdk", - "version": "0.1.1", + "name": "@zoralabs/protocol-sdk", + "version": "0.2.0", "repository": "https://github.com/ourzora/zora-protocol", "license": "MIT", "main": "./dist/index.js", @@ -10,8 +10,7 @@ "build": "tsup", "prepack": "yarn build", "test:js": "vitest src", - "generate-types": "npx openapi-typescript https://api.zora.co/premint/openapi.json -o src/generated/premint-api-types.ts && npx openapi-typescript https://api.zora.co/discover/openapi.json -o src/generated/discover-api-types.ts", - "anvil": "source .env.anvil && anvil --fork-url $FORK_RPC_URL --fork-block-number $FORK_BLOCK_NUMBER --chain-id 31337" + "generate-types": "npx openapi-typescript https://api.zora.co/premint/openapi.json -o src/generated/premint-api-types.ts && npx openapi-typescript https://api.zora.co/discover/openapi.json -o src/generated/discover-api-types.ts" }, "dependencies": { "@zoralabs/protocol-deployments": "*", diff --git a/packages/protocol-sdk/src/anvil.ts b/packages/protocol-sdk/src/anvil.ts new file mode 100644 index 000000000..6e87304ef --- /dev/null +++ b/packages/protocol-sdk/src/anvil.ts @@ -0,0 +1,83 @@ +import { spawn } from "node:child_process"; +import { join } from "path"; +import { test } from "vitest"; +import { + PublicClient, + TestClient, + WalletClient, + createPublicClient, + createTestClient, + createWalletClient, + http, +} from "viem"; +import { foundry } from "viem/chains"; + +export interface AnvilViemClientsTest { + viemClients: { + walletClient: WalletClient; + publicClient: PublicClient; + testClient: TestClient; + }; +} + +async function waitForAnvilInit(anvil: any) { + return new Promise((resolve) => { + anvil.stdout.once("data", () => { + resolve(true); + }); + }); +} + +export const anvilTest = test.extend({ + viemClients: async ({ task }, use) => { + const port = Math.floor(Math.random() * 2000) + 4000; + const anvil = spawn( + "anvil", + [ + "--port", + `${port}`, + "--fork-url", + "https://rpc.zora.co/", + "--fork-block-number", + "6133407", + "--chain-id", + "31337", + ], + { + cwd: join(__dirname, ".."), + killSignal: "SIGINT", + }, + ); + const anvilHost = `http://0.0.0.0:${port}`; + await waitForAnvilInit(anvil); + + const chain = { + ...foundry, + }; + + const walletClient = createWalletClient({ + chain, + transport: http(anvilHost), + }); + + const testClient = createTestClient({ + chain, + mode: "anvil", + transport: http(anvilHost), + }); + + const publicClient = createPublicClient({ + chain, + transport: http(anvilHost), + }); + + await use({ + publicClient, + walletClient, + testClient, + }); + + // clean up function, called once after all tests run + anvil.kill("SIGINT"); + }, +}); diff --git a/packages/protocol-sdk/src/client-base.ts b/packages/protocol-sdk/src/apis/client-base.ts similarity index 98% rename from packages/protocol-sdk/src/client-base.ts rename to packages/protocol-sdk/src/apis/client-base.ts index 5a3a618f3..9aecf2601 100644 --- a/packages/protocol-sdk/src/client-base.ts +++ b/packages/protocol-sdk/src/apis/client-base.ts @@ -1,5 +1,5 @@ import { foundry, zora, zoraTestnet } from "viem/chains"; -import type { BackendChainNames as BackendChainNamesType } from "./premint-api-client"; +import type { BackendChainNames as BackendChainNamesType } from "../premint/premint-api-client"; import { Chain, PublicClient, diff --git a/packages/protocol-sdk/src/generated/discover-api-types.ts b/packages/protocol-sdk/src/apis/generated/discover-api-types.ts similarity index 100% rename from packages/protocol-sdk/src/generated/discover-api-types.ts rename to packages/protocol-sdk/src/apis/generated/discover-api-types.ts diff --git a/packages/protocol-sdk/src/generated/premint-api-types.ts b/packages/protocol-sdk/src/apis/generated/premint-api-types.ts similarity index 100% rename from packages/protocol-sdk/src/generated/premint-api-types.ts rename to packages/protocol-sdk/src/apis/generated/premint-api-types.ts diff --git a/packages/protocol-sdk/src/http-api-base.ts b/packages/protocol-sdk/src/apis/http-api-base.ts similarity index 100% rename from packages/protocol-sdk/src/http-api-base.ts rename to packages/protocol-sdk/src/apis/http-api-base.ts diff --git a/packages/protocol-sdk/src/create-helper.test.ts b/packages/protocol-sdk/src/create/1155-create-helper.test.ts similarity index 99% rename from packages/protocol-sdk/src/create-helper.test.ts rename to packages/protocol-sdk/src/create/1155-create-helper.test.ts index 223f37e8a..cbb7f01fd 100644 --- a/packages/protocol-sdk/src/create-helper.test.ts +++ b/packages/protocol-sdk/src/create/1155-create-helper.test.ts @@ -64,7 +64,7 @@ describe("create-helper", () => { expect(receipt.to).to.equal("0x777777c338d93e2c7adf08d102d45ca7cc4ed021"); expect(getTokenIdFromCreateReceipt(receipt)).to.be.equal(1n); }, - 10 * 1000, + 20 * 1000, ); it( "creates a new contract, than creates a new token on this existing contract", @@ -107,6 +107,6 @@ describe("create-helper", () => { expect(tokenId).to.be.equal(2n); expect(newReceipt).not.toBeNull(); }, - 10 * 1000, + 20 * 1000, ); }); diff --git a/packages/protocol-sdk/src/1155-create-helper.ts b/packages/protocol-sdk/src/create/1155-create-helper.ts similarity index 99% rename from packages/protocol-sdk/src/1155-create-helper.ts rename to packages/protocol-sdk/src/create/1155-create-helper.ts index 1c103c86b..e87a42a1d 100644 --- a/packages/protocol-sdk/src/1155-create-helper.ts +++ b/packages/protocol-sdk/src/create/1155-create-helper.ts @@ -12,7 +12,7 @@ import type { WalletClient, } from "viem"; import { decodeEventLog, encodeFunctionData, zeroAddress } from "viem"; -import { OPEN_EDITION_MINT_SIZE } from "./constants"; +import { OPEN_EDITION_MINT_SIZE } from "../constants"; const saleEndForever = 18446744073709551615n; const royaltyBPSDefault = 1000; diff --git a/packages/protocol-sdk/src/index.ts b/packages/protocol-sdk/src/index.ts index 8304aeb42..ba462be8f 100644 --- a/packages/protocol-sdk/src/index.ts +++ b/packages/protocol-sdk/src/index.ts @@ -1,9 +1,9 @@ -export * from "./premint-client"; +export * from "./premint/premint-client"; -export * from "./preminter"; +export * from "./premint/preminter"; -export * from "./premint-api-client"; +export * from "./premint/premint-api-client"; -export * from "./mint-api-client"; +export * from "./mint/mint-api-client"; -export * from "./1155-create-helper"; +export * from "./create/1155-create-helper"; diff --git a/packages/protocol-sdk/src/mint-api-client.ts b/packages/protocol-sdk/src/mint/mint-api-client.ts similarity index 87% rename from packages/protocol-sdk/src/mint-api-client.ts rename to packages/protocol-sdk/src/mint/mint-api-client.ts index d3d8af6b9..627a8e1cb 100644 --- a/packages/protocol-sdk/src/mint-api-client.ts +++ b/packages/protocol-sdk/src/mint/mint-api-client.ts @@ -1,7 +1,7 @@ -import { retries, get, post } from "./http-api-base"; -import { paths } from "./generated/discover-api-types"; -import { ZORA_API_BASE } from "./premint-api-client"; -import { ZORA_SUBGRAPH_URLS } from "./constants"; +import { retries, get, post } from "../apis/http-api-base"; +import { paths } from "../apis/generated/discover-api-types"; +import { ZORA_API_BASE } from "../premint/premint-api-client"; +import { ZORA_SUBGRAPH_URLS } from "../constants"; export type MintableGetToken = paths["/mintables_v2/{chain_name}/{collection_address}"]; diff --git a/packages/protocol-sdk/src/mint-client.test.ts b/packages/protocol-sdk/src/mint/mint-client.test.ts similarity index 77% rename from packages/protocol-sdk/src/mint-client.test.ts rename to packages/protocol-sdk/src/mint/mint-client.test.ts index d94259369..a75cb16c6 100644 --- a/packages/protocol-sdk/src/mint-client.test.ts +++ b/packages/protocol-sdk/src/mint/mint-client.test.ts @@ -1,53 +1,27 @@ import { - Address, - createPublicClient, - createTestClient, - createWalletClient, - http, parseAbi, parseEther, } from "viem"; -import { foundry, zora } from "viem/chains"; -import { describe, it, beforeEach, expect, afterEach } from "vitest"; +import { zora } from "viem/chains"; +import { describe, expect } from "vitest"; import { MintClient } from "./mint-client"; import { zoraCreator1155ImplABI } from "@zoralabs/protocol-deployments"; - -const chain = foundry; - -const walletClient = createWalletClient({ - chain, - transport: http(), -}); - -const testClient = createTestClient({ - chain, - mode: "anvil", - transport: http(), -}); - -const publicClient = createPublicClient({ - chain, - transport: http(), -}); - -const [creatorAccount] = (await walletClient.getAddresses()) as [Address]; +import { anvilTest } from "src/anvil"; const erc721ABI = parseAbi([ "function balanceOf(address owner) public view returns (uint256)", ] as const); describe("mint-helper", () => { - beforeEach(async () => { - await testClient.setBalance({ - address: creatorAccount, - value: parseEther("2000"), - }); - }); - afterEach(() => testClient.reset()); - - it( + anvilTest( "mints a new 1155 token", - async () => { + async ({ viemClients }) => { + const { testClient, walletClient, publicClient } = viemClients; + const creatorAccount = (await walletClient.getAddresses())[0]!; + await testClient.setBalance({ + address: creatorAccount, + value: parseEther("2000"), + }); const targetContract = "0xa2fea3537915dc6c7c7a97a82d1236041e6feb2e"; const targetTokenId = 1n; const minter = new MintClient(zora); @@ -84,9 +58,16 @@ describe("mint-helper", () => { 12 * 1000, ); - it( + anvilTest( "mints a new 721 token", - async () => { + async ({ viemClients }) => { + const { testClient, walletClient, publicClient } = viemClients; + const creatorAccount = (await walletClient.getAddresses())[0]!; + await testClient.setBalance({ + address: creatorAccount, + value: parseEther("2000"), + }); + const targetContract = "0x7aae7e67515A2CbB8585C707Ca6db37BDd3EA839"; const targetTokenId = undefined; const minter = new MintClient(zora); diff --git a/packages/protocol-sdk/src/mint-client.ts b/packages/protocol-sdk/src/mint/mint-client.ts similarity index 99% rename from packages/protocol-sdk/src/mint-client.ts rename to packages/protocol-sdk/src/mint/mint-client.ts index e37145d80..0aba0159e 100644 --- a/packages/protocol-sdk/src/mint-client.ts +++ b/packages/protocol-sdk/src/mint/mint-client.ts @@ -8,7 +8,7 @@ import { parseAbiParameters, zeroAddress, } from "viem"; -import { ClientBase } from "./client-base"; +import { ClientBase } from "../apis/client-base"; import { MintAPIClient, MintableGetTokenResponse } from "./mint-api-client"; import { zoraCreator1155ImplABI, diff --git a/packages/protocol-sdk/src/premint-api-client.ts b/packages/protocol-sdk/src/premint/premint-api-client.ts similarity index 91% rename from packages/protocol-sdk/src/premint-api-client.ts rename to packages/protocol-sdk/src/premint/premint-api-client.ts index 7db0ac7fd..c4be78df2 100644 --- a/packages/protocol-sdk/src/premint-api-client.ts +++ b/packages/protocol-sdk/src/premint/premint-api-client.ts @@ -1,6 +1,6 @@ -import { post, retries, get } from "./http-api-base"; -import { components, paths } from "./generated/premint-api-types"; -import { ZORA_API_BASE } from "./constants"; +import { post, retries, get } from "../apis/http-api-base"; +import { components, paths } from "../apis/generated/premint-api-types"; +import { ZORA_API_BASE } from "../constants"; type SignaturePostType = paths["/signature"]["post"]; type PremintSignatureRequestBody = diff --git a/packages/protocol-sdk/src/premint-client.test.ts b/packages/protocol-sdk/src/premint/premint-client.test.ts similarity index 63% rename from packages/protocol-sdk/src/premint-client.test.ts rename to packages/protocol-sdk/src/premint/premint-client.test.ts index a76e3c06c..290e08d1f 100644 --- a/packages/protocol-sdk/src/premint-client.test.ts +++ b/packages/protocol-sdk/src/premint/premint-client.test.ts @@ -1,62 +1,15 @@ -import { - createTestClient, - http, - createWalletClient, - createPublicClient, - Address, -} from "viem"; import { foundry } from "viem/chains"; -import { describe, it, beforeEach, expect, vi, afterEach } from "vitest"; -import { parseEther } from "viem"; +import { describe, expect, vi } from "vitest"; import { PremintClient } from "./premint-client"; -import { BackendChainNamesLookup } from "./client-base"; - -const chain = foundry; - -const walletClient = createWalletClient({ - chain, - transport: http(), -}); - -const testClient = createTestClient({ - chain, - mode: "anvil", - transport: http(), -}); - -const publicClient = createPublicClient({ - chain, - transport: http(), -}); - -// JSON-RPC Account -const [deployerAccount, secondWallet] = (await walletClient.getAddresses()) as [ - Address, - Address -]; +import { BackendChainNamesLookup } from "../apis/client-base"; +import { anvilTest } from "src/anvil"; describe("ZoraCreator1155Premint", () => { - beforeEach(async () => { - // deploy signature minter contract - await testClient.setBalance({ - address: deployerAccount, - value: parseEther("1"), - }); - - await testClient.setBalance({ - address: secondWallet, - value: parseEther("1"), - }); - }, 20 * 1000); - - afterEach(() => { - testClient.reset(); - }, 4 * 1000); - - it( + anvilTest( "can sign on the forked premint contract", - async () => { - const premintClient = new PremintClient(chain); + async ({ viemClients: { walletClient, publicClient } }) => { + const [deployerAccount] = await walletClient.getAddresses(); + const premintClient = new PremintClient(foundry); premintClient.apiClient.getNextUID = vi .fn() @@ -68,10 +21,10 @@ describe("ZoraCreator1155Premint", () => { await premintClient.createPremint({ walletClient, publicClient, - account: deployerAccount, + account: deployerAccount!, checkSignature: true, collection: { - contractAdmin: deployerAccount, + contractAdmin: deployerAccount!, contractName: "Testing Contract", contractURI: "ipfs://bafkreiainxen4b4wz4ubylvbhons6rembxdet4a262nf2lziclqvv7au3e", @@ -112,57 +65,58 @@ describe("ZoraCreator1155Premint", () => { "0x588d19641de9ba1dade4d2bb5387c8dc96f4a990fef69787534b60caead759e6334975a6be10a796da948cd7d1d4f5580b3f84d49d9fa4e0b41c97759507975a1c", }); }, - 20 * 1000 + 20 * 1000, ); - it("can validate premint on network", async () => { - const premintClient = new PremintClient(chain); + anvilTest( + "can validate premint on network", + async ({ viemClients: { publicClient } }) => { + const premintClient = new PremintClient(foundry); - const premintData = { - collection: { - contractAdmin: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", - contractName: "Testing Contract", - contractURI: - "ipfs://bafkreiainxen4b4wz4ubylvbhons6rembxdet4a262nf2lziclqvv7au3e", - }, - premint: { - uid: 3, - version: 1, - deleted: false, - tokenConfig: { - maxSupply: "18446744073709551615", - maxTokensPerAddress: "0", - pricePerToken: "0", - mintDuration: "604800", - mintStart: "0", - royaltyMintSchedule: 0, - royaltyBPS: 1000, - fixedPriceMinter: "0x04E2516A2c207E84a1839755675dfd8eF6302F0a", - royaltyRecipient: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", - tokenURI: - "ipfs://bafkreice23maski3x52tsfqgxstx3kbiifnt5jotg3a5ynvve53c4soi2u", + const premintData = { + collection: { + contractAdmin: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + contractName: "Testing Contract", + contractURI: + "ipfs://bafkreiainxen4b4wz4ubylvbhons6rembxdet4a262nf2lziclqvv7au3e", }, - }, - chain_name: "ZORA-TESTNET", - signature: - "0x588d19641de9ba1dade4d2bb5387c8dc96f4a990fef69787534b60caead759e6334975a6be10a796da948cd7d1d4f5580b3f84d49d9fa4e0b41c97759507975a1c", - } as const; - const publicClient = createPublicClient({ - chain: foundry, - transport: http(), - }); - const signatureValid = await premintClient.isValidSignature({ - // @ts-ignore: Fix enum type - data: premintData, - publicClient, - }); - expect(signatureValid.isValid).toBe(true); - }); + premint: { + uid: 3, + version: 1, + deleted: false, + tokenConfig: { + maxSupply: "18446744073709551615", + maxTokensPerAddress: "0", + pricePerToken: "0", + mintDuration: "604800", + mintStart: "0", + royaltyMintSchedule: 0, + royaltyBPS: 1000, + fixedPriceMinter: "0x04E2516A2c207E84a1839755675dfd8eF6302F0a", + royaltyRecipient: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + tokenURI: + "ipfs://bafkreice23maski3x52tsfqgxstx3kbiifnt5jotg3a5ynvve53c4soi2u", + }, + }, + chain_name: "ZORA-TESTNET", + signature: + "0x588d19641de9ba1dade4d2bb5387c8dc96f4a990fef69787534b60caead759e6334975a6be10a796da948cd7d1d4f5580b3f84d49d9fa4e0b41c97759507975a1c", + } as const; + + const signatureValid = await premintClient.isValidSignature({ + // @ts-ignore: Fix enum type + data: premintData, + publicClient, + }); + expect(signatureValid.isValid).toBe(true); + }, + ); - it( + anvilTest( "can execute premint on network", - async () => { - const premintClient = new PremintClient(chain); + async ({ viemClients: { walletClient, publicClient } }) => { + const [deployerAccount] = await walletClient.getAddresses(); + const premintClient = new PremintClient(foundry); premintClient.apiClient.getSignature = vi.fn().mockResolvedValue({ chain_name: "ZORA-TESTNET", @@ -237,6 +191,6 @@ describe("ZoraCreator1155Premint", () => { uid: 3, }); }, - 20 * 1000 + 20 * 1000, ); }); diff --git a/packages/protocol-sdk/src/premint-client.ts b/packages/protocol-sdk/src/premint/premint-client.ts similarity index 99% rename from packages/protocol-sdk/src/premint-client.ts rename to packages/protocol-sdk/src/premint/premint-client.ts index affa86032..460bbe7f2 100644 --- a/packages/protocol-sdk/src/premint-client.ts +++ b/packages/protocol-sdk/src/premint/premint-client.ts @@ -20,8 +20,8 @@ import type { } from "./premint-api-client"; import { PremintAPIClient } from "./premint-api-client"; import type { DecodeEventLogReturnType } from "viem"; -import { ClientBase, REWARD_PER_TOKEN } from "./client-base"; -import { OPEN_EDITION_MINT_SIZE } from "./constants"; +import { ClientBase, REWARD_PER_TOKEN } from "../apis/client-base"; +import { OPEN_EDITION_MINT_SIZE } from "../constants"; type MintArgumentsSettings = { tokenURI: string; diff --git a/packages/protocol-sdk/src/premint/preminter.test.ts b/packages/protocol-sdk/src/premint/preminter.test.ts new file mode 100644 index 000000000..c5e7b38f7 --- /dev/null +++ b/packages/protocol-sdk/src/premint/preminter.test.ts @@ -0,0 +1,522 @@ +import { + createTestClient, + http, + createWalletClient, + createPublicClient, + keccak256, + Hex, + concat, + recoverAddress, + hashDomain, +} from "viem"; +import { foundry, zora } from "viem/chains"; +import { describe, it, beforeEach, expect, afterEach } from "vitest"; +import { parseEther } from "viem"; +import { + zoraCreator1155PremintExecutorImplABI as preminterAbi, + zoraCreator1155PremintExecutorImplAddress as zoraCreator1155PremintExecutorAddress, + zoraCreator1155ImplABI, + zoraCreator1155FactoryImplAddress, + zoraCreator1155FactoryImplConfig, +} from "@zoralabs/protocol-deployments"; + +import { + ContractCreationConfig, + PremintConfig, + TokenCreationConfig, + preminterTypedDataDefinition, +} from "./preminter"; +import { AnvilViemClientsTest, anvilTest } from "src/anvil"; + +type Address = `0x${string}`; + +type TestContext = { + preminterAddress: `0x${string}`; + forkedChainId: keyof typeof zoraCreator1155FactoryImplAddress; + anvilChainId: number; + zoraMintFee: bigint; + fixedPriceMinterAddress: Address; +}; + +// create token and contract creation config: +const defaultContractConfig = ({ + contractAdmin, +}: { + contractAdmin: Address; +}): ContractCreationConfig => ({ + contractAdmin, + contractURI: "ipfs://asdfasdfasdf", + contractName: "My fun NFT", +}); + +const defaultTokenConfig = ( + fixedPriceMinterAddress: Address, + creatorAccount: Address, +): TokenCreationConfig => ({ + tokenURI: "ipfs://tokenIpfsId0", + maxSupply: 100n, + maxTokensPerAddress: 10n, + pricePerToken: 0n, + mintStart: 0n, + mintDuration: 100n, + royaltyMintSchedule: 30, + royaltyBPS: 200, + royaltyRecipient: creatorAccount, + fixedPriceMinter: fixedPriceMinterAddress, +}); + +const defaultPremintConfig = ( + fixedPriceMinter: Address, + creatorAccount: Address, +): PremintConfig => ({ + tokenConfig: defaultTokenConfig(fixedPriceMinter, creatorAccount), + deleted: false, + uid: 105, + version: 0, +}); + +const ZORA_MINT_FEE = parseEther("0.000777"); + +const PREMINTER_ADDRESS = zoraCreator1155PremintExecutorAddress[999]; + +async function setupContracts({ + viemClients: { walletClient, testClient, publicClient }, +}: AnvilViemClientsTest) { + // JSON-RPC Account + const [deployerAccount, creatorAccount, collectorAccount] = + (await walletClient.getAddresses()) as [Address, Address, Address, Address]; + + // deploy signature minter contract + await testClient.setBalance({ + address: deployerAccount, + value: parseEther("10"), + }); + + const fixedPriceMinterAddress = await publicClient.readContract({ + abi: zoraCreator1155FactoryImplConfig.abi, + address: zoraCreator1155FactoryImplAddress[999], + functionName: "fixedPriceMinter", + }); + + return { + accounts: { + deployerAccount, + creatorAccount, + collectorAccount, + }, + fixedPriceMinterAddress, + }; +} + +describe("ZoraCreator1155Preminter", () => { + // skip for now - we need to make this work on zora testnet chain too + anvilTest( + "can sign on the forked premint contract", + async ({ viemClients }) => { + const { + fixedPriceMinterAddress, + accounts: { creatorAccount }, + } = await setupContracts({ viemClients }); + const premintConfig = defaultPremintConfig( + fixedPriceMinterAddress, + creatorAccount, + ); + const contractConfig = defaultContractConfig({ + contractAdmin: creatorAccount, + }); + + const preminterAddress = zoraCreator1155PremintExecutorAddress[999]; + + const contractAddress = await viemClients.publicClient.readContract({ + abi: preminterAbi, + address: preminterAddress, + functionName: "getContractAddress", + args: [contractConfig], + }); + + const signedMessage = await viemClients.walletClient.signTypedData({ + ...preminterTypedDataDefinition({ + verifyingContract: contractAddress, + chainId: 999, + premintConfig, + }), + account: creatorAccount, + }); + + console.log({ + creatorAccount, + signedMessage, + contractConfig, + premintConfig, + contractAddress, + }); + }, + 20 * 1000, + ); + anvilTest( + "can sign and recover a signature", + async ({ viemClients }) => { + const { + fixedPriceMinterAddress, + accounts: { creatorAccount }, + } = await setupContracts({ viemClients }); + + const premintConfig = defaultPremintConfig( + fixedPriceMinterAddress, + creatorAccount, + ); + const contractConfig = defaultContractConfig({ + contractAdmin: creatorAccount, + }); + + const contractAddress = await viemClients.publicClient.readContract({ + abi: preminterAbi, + address: PREMINTER_ADDRESS, + functionName: "getContractAddress", + args: [contractConfig], + }); + + // sign message containing contract and token creation config and uid + const signedMessage = await viemClients.walletClient.signTypedData({ + ...preminterTypedDataDefinition({ + verifyingContract: contractAddress, + // we need to sign here for the anvil chain, cause thats where it is run on + chainId: foundry.id, + premintConfig, + }), + account: creatorAccount, + }); + + // recover and verify address is correct + const recoveredAddress = await viemClients.publicClient.readContract({ + abi: preminterAbi, + address: PREMINTER_ADDRESS, + functionName: "recoverSigner", + args: [premintConfig, contractAddress, signedMessage], + }); + + expect(recoveredAddress).to.equal(creatorAccount); + }, + + 20 * 1000, + ); + anvilTest( + "can sign and mint multiple tokens", + async ({ viemClients }) => { + const { + fixedPriceMinterAddress, + accounts: { creatorAccount, collectorAccount }, + } = await setupContracts({ viemClients }); + // setup contract and token creation parameters + const premintConfig = defaultPremintConfig( + fixedPriceMinterAddress, + creatorAccount, + ); + const contractConfig = defaultContractConfig({ + contractAdmin: creatorAccount, + }); + + // lets make it a random number to not break the existing tests that expect fresh data + premintConfig.uid = Math.round(Math.random() * 1000000); + + let contractAddress = await viemClients.publicClient.readContract({ + abi: preminterAbi, + address: PREMINTER_ADDRESS, + functionName: "getContractAddress", + args: [contractConfig], + }); + + // have creator sign the message to create the contract + // and the token + const signedMessage = await viemClients.walletClient.signTypedData({ + ...preminterTypedDataDefinition({ + verifyingContract: contractAddress, + // we need to sign here for the anvil chain, cause thats where it is run on + chainId: foundry.id, + premintConfig, + }), + account: creatorAccount, + }); + + const quantityToMint = 2n; + + const valueToSend = + (ZORA_MINT_FEE + premintConfig.tokenConfig.pricePerToken) * + quantityToMint; + + const comment = "I love this!"; + + await viemClients.testClient.setBalance({ + address: collectorAccount, + value: parseEther("10"), + }); + + // get the premint status - it should not be minted + let [contractCreated, tokenId] = + await viemClients.publicClient.readContract({ + abi: preminterAbi, + address: PREMINTER_ADDRESS, + functionName: "premintStatus", + args: [contractAddress, premintConfig.uid], + }); + + expect(contractCreated).toBe(false); + expect(tokenId).toBe(0n); + + // now have the collector execute the first signed message; + // it should create the contract, the token, + // and min the quantity to mint tokens to the collector + // the signature along with contract + token creation + // parameters are required to call this function + const mintHash = await viemClients.walletClient.writeContract({ + abi: preminterAbi, + functionName: "premint", + account: collectorAccount, + chain: foundry, + address: PREMINTER_ADDRESS, + args: [ + contractConfig, + premintConfig, + signedMessage, + quantityToMint, + comment, + ], + value: valueToSend, + }); + + // ensure it succeeded + const receipt = await viemClients.publicClient.waitForTransactionReceipt({ + hash: mintHash, + }); + + expect(receipt.status).toBe("success"); + + // fetch the premint token id + [contractCreated, tokenId] = await viemClients.publicClient.readContract({ + abi: preminterAbi, + address: PREMINTER_ADDRESS, + functionName: "premintStatus", + args: [contractAddress, premintConfig.uid], + }); + + expect(tokenId).not.toBe(0n); + + // now use what was created, to get the balance from the created contract + const tokenBalance = await viemClients.publicClient.readContract({ + abi: zoraCreator1155ImplABI, + address: contractAddress, + functionName: "balanceOf", + args: [collectorAccount, tokenId], + }); + + // get token balance - should be amount that was created + expect(tokenBalance).toBe(quantityToMint); + + const premintConfig2 = { + ...premintConfig, + uid: premintConfig.uid + 1, + tokenConfig: { + ...premintConfig.tokenConfig, + tokenURI: "ipfs://tokenIpfsId2", + pricePerToken: parseEther("0.05"), + }, + }; + + // sign the message to create the second token + const signedMessage2 = await viemClients.walletClient.signTypedData({ + ...preminterTypedDataDefinition({ + verifyingContract: contractAddress, + chainId: foundry.id, + premintConfig: premintConfig2, + }), + account: creatorAccount, + }); + + const quantityToMint2 = 4n; + + const valueToSend2 = + (ZORA_MINT_FEE + premintConfig2.tokenConfig.pricePerToken) * + quantityToMint2; + + // now have the collector execute the second signed message. + // it should create a new token against the existing contract + const mintHash2 = await viemClients.walletClient.writeContract({ + abi: preminterAbi, + functionName: "premint", + account: collectorAccount, + chain: foundry, + address: PREMINTER_ADDRESS, + args: [ + contractConfig, + premintConfig2, + signedMessage2, + quantityToMint2, + comment, + ], + value: valueToSend2, + }); + + expect( + ( + await viemClients.publicClient.waitForTransactionReceipt({ + hash: mintHash2, + }) + ).status, + ).toBe("success"); + + // now premint status for the second mint, it should be minted + [, tokenId] = await viemClients.publicClient.readContract({ + abi: preminterAbi, + address: PREMINTER_ADDRESS, + functionName: "premintStatus", + args: [contractAddress, premintConfig2.uid], + }); + + expect(tokenId).not.toBe(0n); + + // get balance of second token + const tokenBalance2 = await viemClients.publicClient.readContract({ + abi: zoraCreator1155ImplABI, + address: contractAddress, + functionName: "balanceOf", + args: [collectorAccount, tokenId], + }); + + expect(tokenBalance2).toBe(quantityToMint2); + }, + // 10 second timeout + 40 * 1000, + ); + + anvilTest( + "can decode the CreatorAttribution event", + async ({ viemClients }) => { + const { + fixedPriceMinterAddress, + accounts: { creatorAccount, collectorAccount }, + } = await setupContracts({ viemClients }); + const premintConfig = defaultPremintConfig( + fixedPriceMinterAddress, + creatorAccount, + ); + const contractConfig = defaultContractConfig({ + contractAdmin: creatorAccount, + }); + + // lets make it a random number to not break the existing tests that expect fresh data + premintConfig.uid = Math.round(Math.random() * 1000000); + + let contractAddress = await viemClients.publicClient.readContract({ + abi: preminterAbi, + address: PREMINTER_ADDRESS, + functionName: "getContractAddress", + args: [contractConfig], + }); + + // have creator sign the message to create the contract + // and the token + const signedMessage = await viemClients.walletClient.signTypedData({ + ...preminterTypedDataDefinition({ + verifyingContract: contractAddress, + // we need to sign here for the anvil chain, cause thats where it is run on + chainId: foundry.id, + premintConfig, + }), + account: creatorAccount, + }); + + const quantityToMint = 2n; + + const valueToSend = + (ZORA_MINT_FEE + premintConfig.tokenConfig.pricePerToken) * + quantityToMint; + + const comment = "I love this!"; + + await viemClients.testClient.setBalance({ + address: collectorAccount, + value: parseEther("10"), + }); + + // now have the collector execute the first signed message; + // it should create the contract, the token, + // and min the quantity to mint tokens to the collector + // the signature along with contract + token creation + // parameters are required to call this function + const mintHash = await viemClients.walletClient.writeContract({ + abi: preminterAbi, + functionName: "premint", + account: collectorAccount, + chain: foundry, + address: PREMINTER_ADDRESS, + args: [ + contractConfig, + premintConfig, + signedMessage, + quantityToMint, + comment, + ], + value: valueToSend, + }); + + // ensure it succeeded + const receipt = await viemClients.publicClient.waitForTransactionReceipt({ + hash: mintHash, + }); + + expect(receipt.status).toBe("success"); + + // get the CreatorAttribution event from the erc1155 contract: + const topics = await viemClients.publicClient.getContractEvents({ + abi: zoraCreator1155ImplABI, + address: contractAddress, + eventName: "CreatorAttribution", + }); + + expect(topics.length).toBe(1); + + const creatorAttributionEvent = topics[0]!; + + const { creator, domainName, signature, structHash, version } = + creatorAttributionEvent.args; + + const chainId = foundry.id; + + // hash the eip712 domain based on the parameters emitted from the event: + const hashedDomain = hashDomain({ + domain: { + chainId, + name: domainName, + verifyingContract: contractAddress, + version, + }, + types: { + EIP712Domain: [ + { name: "name", type: "string" }, + { name: "version", type: "string" }, + { + name: "chainId", + type: "uint256", + }, + { + name: "verifyingContract", + type: "address", + }, + ], + }, + }); + + // re-build the eip-712 typed data hash, consisting of the hashed domain and the structHash emitted from the event: + const parts: Hex[] = ["0x1901", hashedDomain, structHash!]; + + const hashedTypedData = keccak256(concat(parts)); + + const recoveredSigner = await recoverAddress({ + hash: hashedTypedData, + signature: signature!, + }); + + expect(recoveredSigner).toBe(creator); + }, + ); +}); diff --git a/packages/protocol-sdk/src/preminter.ts b/packages/protocol-sdk/src/premint/preminter.ts similarity index 100% rename from packages/protocol-sdk/src/preminter.ts rename to packages/protocol-sdk/src/premint/preminter.ts diff --git a/packages/protocol-sdk/src/preminter.test.ts b/packages/protocol-sdk/src/preminter.test.ts deleted file mode 100644 index 5065842af..000000000 --- a/packages/protocol-sdk/src/preminter.test.ts +++ /dev/null @@ -1,510 +0,0 @@ -import { - createTestClient, - http, - createWalletClient, - createPublicClient, - keccak256, - Hex, - concat, - recoverAddress, - hashDomain, -} from "viem"; -import { foundry, zora } from "viem/chains"; -import { describe, it, beforeEach, expect, afterEach } from "vitest"; -import { parseEther } from "viem"; -import { - zoraCreator1155PremintExecutorImplABI as preminterAbi, - zoraCreator1155PremintExecutorImplAddress as zoraCreator1155PremintExecutorAddress, - zoraCreator1155ImplABI, - zoraCreator1155FactoryImplAddress, - zoraCreator1155FactoryImplConfig, -} from "@zoralabs/protocol-deployments"; - -import { - ContractCreationConfig, - PremintConfig, - TokenCreationConfig, - preminterTypedDataDefinition, -} from "./preminter"; - -const walletClient = createWalletClient({ - chain: foundry, - transport: http(), -}); - -export const walletClientWithAccount = createWalletClient({ - chain: foundry, - transport: http(), -}); - -const testClient = createTestClient({ - chain: foundry, - mode: "anvil", - transport: http(), -}); - -const publicClient = createPublicClient({ - chain: foundry, - transport: http(), -}); - -type Address = `0x${string}`; - -// JSON-RPC Account -const [deployerAccount, creatorAccount, collectorAccount] = - (await walletClient.getAddresses()) as [Address, Address, Address, Address]; - -type TestContext = { - preminterAddress: `0x${string}`; - forkedChainId: keyof typeof zoraCreator1155FactoryImplAddress; - anvilChainId: number; - zoraMintFee: bigint; - fixedPriceMinterAddress: Address; -}; - -// create token and contract creation config: -const defaultContractConfig = ({ - contractAdmin, -}: { - contractAdmin: Address; -}): ContractCreationConfig => ({ - contractAdmin, - contractURI: "ipfs://asdfasdfasdf", - contractName: "My fun NFT", -}); - -const defaultTokenConfig = ( - fixedPriceMinterAddress: Address, -): TokenCreationConfig => ({ - tokenURI: "ipfs://tokenIpfsId0", - maxSupply: 100n, - maxTokensPerAddress: 10n, - pricePerToken: 0n, - mintStart: 0n, - mintDuration: 100n, - royaltyMintSchedule: 30, - royaltyBPS: 200, - royaltyRecipient: creatorAccount, - fixedPriceMinter: fixedPriceMinterAddress, -}); - -const defaultPremintConfig = (fixedPriceMinter: Address): PremintConfig => ({ - tokenConfig: defaultTokenConfig(fixedPriceMinter), - deleted: false, - uid: 105, - version: 0, -}); - -describe("ZoraCreator1155Preminter", () => { - beforeEach(async (ctx) => { - // deploy signature minter contract - await testClient.setBalance({ - address: deployerAccount, - value: parseEther("10"), - }); - - ctx.forkedChainId = zora.id; - ctx.anvilChainId = foundry.id; - - ctx.fixedPriceMinterAddress = await publicClient.readContract({ - abi: zoraCreator1155FactoryImplConfig.abi, - address: zoraCreator1155FactoryImplAddress[ctx.forkedChainId], - functionName: "fixedPriceMinter", - }); - ctx.zoraMintFee = parseEther("0.000777"); - - ctx.preminterAddress = - zoraCreator1155PremintExecutorAddress[ctx.forkedChainId]; - }, 20 * 1000); - - afterEach(() => { - testClient.reset(); - }, 4 * 1000); - - // skip for now - we need to make this work on zora testnet chain too - it( - "can sign on the forked premint contract", - async ({ fixedPriceMinterAddress, forkedChainId }) => { - const premintConfig = defaultPremintConfig(fixedPriceMinterAddress); - const contractConfig = defaultContractConfig({ - contractAdmin: creatorAccount, - }); - - const preminterAddress = zoraCreator1155PremintExecutorAddress[ - forkedChainId as keyof typeof zoraCreator1155PremintExecutorAddress - ] as Address; - - const contractAddress = await publicClient.readContract({ - abi: preminterAbi, - address: preminterAddress, - functionName: "getContractAddress", - args: [contractConfig], - }); - - const signedMessage = await walletClient.signTypedData({ - ...preminterTypedDataDefinition({ - verifyingContract: contractAddress, - chainId: 999, - premintConfig, - }), - account: creatorAccount, - }); - - console.log({ - creatorAccount, - signedMessage, - contractConfig, - premintConfig, - contractAddress, - }); - }, - 20 * 1000, - ); - it( - "can sign and recover a signature", - async ({ - preminterAddress: preminterAddress, - anvilChainId, - fixedPriceMinterAddress, - }) => { - const premintConfig = defaultPremintConfig(fixedPriceMinterAddress); - const contractConfig = defaultContractConfig({ - contractAdmin: creatorAccount, - }); - - const contractAddress = await publicClient.readContract({ - abi: preminterAbi, - address: preminterAddress, - functionName: "getContractAddress", - args: [contractConfig], - }); - - // sign message containing contract and token creation config and uid - const signedMessage = await walletClient.signTypedData({ - ...preminterTypedDataDefinition({ - verifyingContract: contractAddress, - // we need to sign here for the anvil chain, cause thats where it is run on - chainId: anvilChainId, - premintConfig, - }), - account: creatorAccount, - }); - - // recover and verify address is correct - const recoveredAddress = await publicClient.readContract({ - abi: preminterAbi, - address: preminterAddress, - functionName: "recoverSigner", - args: [premintConfig, contractAddress, signedMessage], - }); - - expect(recoveredAddress).to.equal(creatorAccount); - }, - - 20 * 1000, - ); - it( - "can sign and mint multiple tokens", - async ({ - zoraMintFee, - anvilChainId, - preminterAddress: preminterAddress, - fixedPriceMinterAddress, - }) => { - // setup contract and token creation parameters - const premintConfig = defaultPremintConfig(fixedPriceMinterAddress); - const contractConfig = defaultContractConfig({ - contractAdmin: creatorAccount, - }); - - // lets make it a random number to not break the existing tests that expect fresh data - premintConfig.uid = Math.round(Math.random() * 1000000); - - let contractAddress = await publicClient.readContract({ - abi: preminterAbi, - address: preminterAddress, - functionName: "getContractAddress", - args: [contractConfig], - }); - - // have creator sign the message to create the contract - // and the token - const signedMessage = await walletClient.signTypedData({ - ...preminterTypedDataDefinition({ - verifyingContract: contractAddress, - // we need to sign here for the anvil chain, cause thats where it is run on - chainId: anvilChainId, - premintConfig, - }), - account: creatorAccount, - }); - - const quantityToMint = 2n; - - const valueToSend = - (zoraMintFee + premintConfig.tokenConfig.pricePerToken) * - quantityToMint; - - const comment = "I love this!"; - - await testClient.setBalance({ - address: collectorAccount, - value: parseEther("10"), - }); - - // get the premint status - it should not be minted - let [contractCreated, tokenId] = await publicClient.readContract({ - abi: preminterAbi, - address: preminterAddress, - functionName: "premintStatus", - args: [contractAddress, premintConfig.uid], - }); - - expect(contractCreated).toBe(false); - expect(tokenId).toBe(0n); - - // now have the collector execute the first signed message; - // it should create the contract, the token, - // and min the quantity to mint tokens to the collector - // the signature along with contract + token creation - // parameters are required to call this function - const mintHash = await walletClient.writeContract({ - abi: preminterAbi, - functionName: "premint", - account: collectorAccount, - address: preminterAddress, - args: [ - contractConfig, - premintConfig, - signedMessage, - quantityToMint, - comment, - ], - value: valueToSend, - }); - - // ensure it succeeded - const receipt = await publicClient.waitForTransactionReceipt({ - hash: mintHash, - }); - - expect(receipt.status).toBe("success"); - - // fetch the premint token id - [contractCreated, tokenId] = await publicClient.readContract({ - abi: preminterAbi, - address: preminterAddress, - functionName: "premintStatus", - args: [contractAddress, premintConfig.uid], - }); - - expect(tokenId).not.toBe(0n); - - // now use what was created, to get the balance from the created contract - const tokenBalance = await publicClient.readContract({ - abi: zoraCreator1155ImplABI, - address: contractAddress, - functionName: "balanceOf", - args: [collectorAccount, tokenId], - }); - - // get token balance - should be amount that was created - expect(tokenBalance).toBe(quantityToMint); - - const premintConfig2 = { - ...premintConfig, - uid: premintConfig.uid + 1, - tokenConfig: { - ...premintConfig.tokenConfig, - tokenURI: "ipfs://tokenIpfsId2", - pricePerToken: parseEther("0.05"), - }, - }; - - // sign the message to create the second token - const signedMessage2 = await walletClient.signTypedData({ - ...preminterTypedDataDefinition({ - verifyingContract: contractAddress, - chainId: foundry.id, - premintConfig: premintConfig2, - }), - account: creatorAccount, - }); - - const quantityToMint2 = 4n; - - const valueToSend2 = - (zoraMintFee + premintConfig2.tokenConfig.pricePerToken) * - quantityToMint2; - - // now have the collector execute the second signed message. - // it should create a new token against the existing contract - const mintHash2 = await walletClient.writeContract({ - abi: preminterAbi, - functionName: "premint", - account: collectorAccount, - address: preminterAddress, - args: [ - contractConfig, - premintConfig2, - signedMessage2, - quantityToMint2, - comment, - ], - value: valueToSend2, - }); - - expect( - (await publicClient.waitForTransactionReceipt({ hash: mintHash2 })) - .status, - ).toBe("success"); - - // now premint status for the second mint, it should be minted - [, tokenId] = await publicClient.readContract({ - abi: preminterAbi, - address: preminterAddress, - functionName: "premintStatus", - args: [contractAddress, premintConfig2.uid], - }); - - expect(tokenId).not.toBe(0n); - - // get balance of second token - const tokenBalance2 = await publicClient.readContract({ - abi: zoraCreator1155ImplABI, - address: contractAddress, - functionName: "balanceOf", - args: [collectorAccount, tokenId], - }); - - expect(tokenBalance2).toBe(quantityToMint2); - }, - // 10 second timeout - 40 * 1000, - ); - - it("can decode the CreatorAttribution event", async ({ - zoraMintFee, - anvilChainId, - preminterAddress: preminterAddress, - fixedPriceMinterAddress, - }) => { - const premintConfig = defaultPremintConfig(fixedPriceMinterAddress); - const contractConfig = defaultContractConfig({ - contractAdmin: creatorAccount, - }); - - // lets make it a random number to not break the existing tests that expect fresh data - premintConfig.uid = Math.round(Math.random() * 1000000); - - let contractAddress = await publicClient.readContract({ - abi: preminterAbi, - address: preminterAddress, - functionName: "getContractAddress", - args: [contractConfig], - }); - - // have creator sign the message to create the contract - // and the token - const signedMessage = await walletClient.signTypedData({ - ...preminterTypedDataDefinition({ - verifyingContract: contractAddress, - // we need to sign here for the anvil chain, cause thats where it is run on - chainId: anvilChainId, - premintConfig, - }), - account: creatorAccount, - }); - - const quantityToMint = 2n; - - const valueToSend = - (zoraMintFee + premintConfig.tokenConfig.pricePerToken) * quantityToMint; - - const comment = "I love this!"; - - await testClient.setBalance({ - address: collectorAccount, - value: parseEther("10"), - }); - - // now have the collector execute the first signed message; - // it should create the contract, the token, - // and min the quantity to mint tokens to the collector - // the signature along with contract + token creation - // parameters are required to call this function - const mintHash = await walletClient.writeContract({ - abi: preminterAbi, - functionName: "premint", - account: collectorAccount, - address: preminterAddress, - args: [ - contractConfig, - premintConfig, - signedMessage, - quantityToMint, - comment, - ], - value: valueToSend, - }); - - // ensure it succeeded - const receipt = await publicClient.waitForTransactionReceipt({ - hash: mintHash, - }); - - expect(receipt.status).toBe("success"); - - // get the CreatorAttribution event from the erc1155 contract: - const topics = await publicClient.getContractEvents({ - abi: zoraCreator1155ImplABI, - address: contractAddress, - eventName: "CreatorAttribution", - }); - - expect(topics.length).toBe(1); - - const creatorAttributionEvent = topics[0]!; - - const { creator, domainName, signature, structHash, version } = - creatorAttributionEvent.args; - - const chainId = anvilChainId; - - // hash the eip712 domain based on the parameters emitted from the event: - const hashedDomain = hashDomain({ - domain: { - chainId, - name: domainName, - verifyingContract: contractAddress, - version, - }, - types: { - EIP712Domain: [ - { name: "name", type: "string" }, - { name: "version", type: "string" }, - { - name: "chainId", - type: "uint256", - }, - { - name: "verifyingContract", - type: "address", - }, - ], - }, - }); - - // re-build the eip-712 typed data hash, consisting of the hashed domain and the structHash emitted from the event: - const parts: Hex[] = ["0x1901", hashedDomain, structHash!]; - - const hashedTypedData = keccak256(concat(parts)); - - const recoveredSigner = await recoverAddress({ - hash: hashedTypedData, - signature: signature!, - }); - - expect(recoveredSigner).toBe(creator); - }); -}); diff --git a/packages/protocol-sdk/tsconfig.json b/packages/protocol-sdk/tsconfig.json index 3da7aa253..1c35d0c9d 100644 --- a/packages/protocol-sdk/tsconfig.json +++ b/packages/protocol-sdk/tsconfig.json @@ -21,5 +21,5 @@ "outDir": "dist" }, "exclude": ["node_modules/**", "dist/**"], - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts", "vitest.config.ts"] } From 07d6793db45764ba7eb0922b3f9ff59b44965344 Mon Sep 17 00:00:00 2001 From: iain nash Date: Fri, 3 Nov 2023 02:00:59 -0400 Subject: [PATCH 2/3] finalize all testing --- .github/workflows/foundry.yml | 16 +++++ packages/protocol-sdk/src/anvil.ts | 3 +- .../src/create/1155-create-helper.test.ts | 72 +++++++------------ .../src/premint/preminter.test.ts | 26 +------ turbo.json | 5 +- 5 files changed, 50 insertions(+), 72 deletions(-) diff --git a/.github/workflows/foundry.yml b/.github/workflows/foundry.yml index ad7a2eb47..73108fe93 100644 --- a/.github/workflows/foundry.yml +++ b/.github/workflows/foundry.yml @@ -135,6 +135,22 @@ jobs: run: | npx turbo run build + test_js: + strategy: + fail-fast: true + + name: Test js package + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install node deps and founry + uses: ./.github/actions/setup_deps + + - name: Test js package + run: | + npx turbo run test:js + coverage-1155: uses: ./.github/workflows/coverage.yml name: "Test coverage - 1155" diff --git a/packages/protocol-sdk/src/anvil.ts b/packages/protocol-sdk/src/anvil.ts index 6e87304ef..60e1d8bff 100644 --- a/packages/protocol-sdk/src/anvil.ts +++ b/packages/protocol-sdk/src/anvil.ts @@ -29,7 +29,8 @@ async function waitForAnvilInit(anvil: any) { } export const anvilTest = test.extend({ - viemClients: async ({ task }, use) => { + viemClients: async ({task}, use) => { + console.log('setting up clients for ', task.name); const port = Math.floor(Math.random() * 2000) + 4000; const anvil = spawn( "anvil", diff --git a/packages/protocol-sdk/src/create/1155-create-helper.test.ts b/packages/protocol-sdk/src/create/1155-create-helper.test.ts index cbb7f01fd..802c7548a 100644 --- a/packages/protocol-sdk/src/create/1155-create-helper.test.ts +++ b/packages/protocol-sdk/src/create/1155-create-helper.test.ts @@ -1,53 +1,24 @@ -import { - Address, - createPublicClient, - createTestClient, - createWalletClient, - http, - parseEther, -} from "viem"; -import { foundry } from "viem/chains"; -import { describe, it, beforeEach, expect, afterEach } from "vitest"; +import { parseEther } from "viem"; +import { describe, it, expect } from "vitest"; import { createNew1155Token, getTokenIdFromCreateReceipt, } from "./1155-create-helper"; - -const chain = foundry; - -const walletClient = createWalletClient({ - chain, - transport: http(), -}); - -const testClient = createTestClient({ - chain, - mode: "anvil", - transport: http(), -}); - -const publicClient = createPublicClient({ - chain, - transport: http(), -}); - -const [creatorAccount] = (await walletClient.getAddresses()) as [Address]; +import { anvilTest } from "src/anvil"; const demoTokenMetadataURI = "ipfs://DUMMY/token.json"; const demoContractMetadataURI = "ipfs://DUMMY/contract.json"; describe("create-helper", () => { - beforeEach(async () => { - await testClient.setBalance({ - address: creatorAccount, - value: parseEther("1"), - }); - }); - afterEach(() => testClient.reset()); - - it( + anvilTest( "creates a new contract given arguments", - async () => { + async ({ viemClients: { testClient, publicClient, walletClient } }) => { + const addresses = await walletClient.getAddresses(); + const creatorAddress = addresses[0]!; + await testClient.setBalance({ + address: creatorAddress, + value: parseEther("1"), + }); const new1155TokenRequest = await createNew1155Token({ publicClient, contract: { @@ -55,7 +26,7 @@ describe("create-helper", () => { uri: demoContractMetadataURI, }, tokenMetadataURI: demoTokenMetadataURI, - account: creatorAccount, + account: creatorAddress, mintToCreatorCount: 1, }); const hash = await new1155TokenRequest.send(walletClient); @@ -66,20 +37,25 @@ describe("create-helper", () => { }, 20 * 1000, ); - it( + anvilTest( "creates a new contract, than creates a new token on this existing contract", - async () => { + async ({ viemClients: { publicClient, walletClient } }) => { + const addresses = await walletClient.getAddresses(); + const creatorAccount = addresses[0]!; + const new1155TokenRequest = await createNew1155Token({ publicClient, contract: { - name: "testContract", + name: "testContract2", uri: demoContractMetadataURI, }, tokenMetadataURI: demoTokenMetadataURI, account: creatorAccount, mintToCreatorCount: 1, }); - expect(new1155TokenRequest.contractAddress).to.be.equal('0xA72724cC3DcEF210141a1B84C61824074151Dc99'); + expect(new1155TokenRequest.contractAddress).to.be.equal( + "0xb1A8928dF830C21eD682949Aa8A83C1C215194d3", + ); expect(new1155TokenRequest.contractExists).to.be.false; const hash = await new1155TokenRequest.send(walletClient); const receipt = await publicClient.waitForTransactionReceipt({ hash }); @@ -90,14 +66,16 @@ describe("create-helper", () => { const newTokenOnExistingContract = await createNew1155Token({ publicClient, contract: { - name: "testContract", + name: "testContract2", uri: demoContractMetadataURI, }, tokenMetadataURI: demoTokenMetadataURI, account: creatorAccount, mintToCreatorCount: 1, }); - expect(newTokenOnExistingContract.contractAddress).to.be.equal('0xA72724cC3DcEF210141a1B84C61824074151Dc99'); + expect(newTokenOnExistingContract.contractAddress).to.be.equal( + "0xb1A8928dF830C21eD682949Aa8A83C1C215194d3", + ); expect(newTokenOnExistingContract.contractExists).to.be.true; const newHash = await newTokenOnExistingContract.send(walletClient); const newReceipt = await publicClient.waitForTransactionReceipt({ diff --git a/packages/protocol-sdk/src/premint/preminter.test.ts b/packages/protocol-sdk/src/premint/preminter.test.ts index c5e7b38f7..574bcae6a 100644 --- a/packages/protocol-sdk/src/premint/preminter.test.ts +++ b/packages/protocol-sdk/src/premint/preminter.test.ts @@ -1,16 +1,6 @@ -import { - createTestClient, - http, - createWalletClient, - createPublicClient, - keccak256, - Hex, - concat, - recoverAddress, - hashDomain, -} from "viem"; -import { foundry, zora } from "viem/chains"; -import { describe, it, beforeEach, expect, afterEach } from "vitest"; +import { keccak256, Hex, concat, recoverAddress, hashDomain, Address } from "viem"; +import { foundry } from "viem/chains"; +import { describe, expect } from "vitest"; import { parseEther } from "viem"; import { zoraCreator1155PremintExecutorImplABI as preminterAbi, @@ -28,16 +18,6 @@ import { } from "./preminter"; import { AnvilViemClientsTest, anvilTest } from "src/anvil"; -type Address = `0x${string}`; - -type TestContext = { - preminterAddress: `0x${string}`; - forkedChainId: keyof typeof zoraCreator1155FactoryImplAddress; - anvilChainId: number; - zoraMintFee: bigint; - fixedPriceMinterAddress: Address; -}; - // create token and contract creation config: const defaultContractConfig = ({ contractAdmin, diff --git a/turbo.json b/turbo.json index e88ee7159..fb0a342f2 100644 --- a/turbo.json +++ b/turbo.json @@ -15,6 +15,9 @@ "test": { "dependsOn": ["^test"] }, + "test:js": { + "dependsOn": ["^test:js"] + }, "test:fork": { "dependsOn": ["^test:fork"] }, @@ -38,4 +41,4 @@ "persistent": true } } -} \ No newline at end of file +} From 63b32851ea7c05872ad39005991dae8285a5e8ff Mon Sep 17 00:00:00 2001 From: iain nash Date: Fri, 3 Nov 2023 02:11:14 -0400 Subject: [PATCH 3/3] fix build --- .github/workflows/foundry.yml | 12 ------------ .../src/create/1155-create-helper.test.ts | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/foundry.yml b/.github/workflows/foundry.yml index 73108fe93..e864e533d 100644 --- a/.github/workflows/foundry.yml +++ b/.github/workflows/foundry.yml @@ -135,18 +135,6 @@ jobs: run: | npx turbo run build - test_js: - strategy: - fail-fast: true - - name: Test js package - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Install node deps and founry - uses: ./.github/actions/setup_deps - - name: Test js package run: | npx turbo run test:js diff --git a/packages/protocol-sdk/src/create/1155-create-helper.test.ts b/packages/protocol-sdk/src/create/1155-create-helper.test.ts index 802c7548a..bf303cba5 100644 --- a/packages/protocol-sdk/src/create/1155-create-helper.test.ts +++ b/packages/protocol-sdk/src/create/1155-create-helper.test.ts @@ -1,5 +1,5 @@ import { parseEther } from "viem"; -import { describe, it, expect } from "vitest"; +import { describe, expect } from "vitest"; import { createNew1155Token, getTokenIdFromCreateReceipt,