diff --git a/packages/1155-contracts/.env.anvil b/packages/1155-contracts/.env.anvil index 0c2078aa6..ce8d9a6d7 100644 --- a/packages/1155-contracts/.env.anvil +++ b/packages/1155-contracts/.env.anvil @@ -1,2 +1,2 @@ -FORK_RPC_URL="https://testnet.rpc.zora.co/" -FORK_BLOCK_NUMBER=916572 \ No newline at end of file +FORK_RPC_URL="https://rpc.zora.co/" +FORK_BLOCK_NUMBER=5141442 \ No newline at end of file diff --git a/packages/1155-contracts/package.json b/packages/1155-contracts/package.json index c96c59690..cbc425004 100644 --- a/packages/1155-contracts/package.json +++ b/packages/1155-contracts/package.json @@ -58,7 +58,7 @@ "tsup": "^7.2.0", "tsx": "^3.13.0", "typescript": "^5.0.4", - "viem": "^1.6.0", + "viem": "^1.16.2", "vite": "^4.1.4", "vitest": "~0.30.1" }, diff --git a/packages/1155-contracts/package/batchPublish.test.ts b/packages/1155-contracts/package/batchPublish.test.ts index 2820e12c4..c9131e96b 100644 --- a/packages/1155-contracts/package/batchPublish.test.ts +++ b/packages/1155-contracts/package/batchPublish.test.ts @@ -193,7 +193,7 @@ function parseCreate1155Receipt(receipt: TransactionReceipt): { return { tokenId, contractAddress }; } -describe("ZoraCreator1155Preminter", () => { +describe("Zora1155", () => { it( "can batch publish tokens", async () => { diff --git a/packages/1155-contracts/package/preminter.test.ts b/packages/1155-contracts/package/preminter.test.ts index d1ed9cf34..79c94b6d8 100644 --- a/packages/1155-contracts/package/preminter.test.ts +++ b/packages/1155-contracts/package/preminter.test.ts @@ -3,8 +3,13 @@ import { http, createWalletClient, createPublicClient, + keccak256, + Hex, + concat, + recoverAddress, + hashDomain, } from "viem"; -import { foundry, zoraTestnet } from "viem/chains"; +import { foundry, zora } from "viem/chains"; import { describe, it, beforeEach, expect } from "vitest"; import { parseEther } from "viem"; import { @@ -14,12 +19,7 @@ import { zoraCreator1155FactoryImplAddress, zoraCreator1155FactoryImplConfig, } from "./wagmiGenerated"; -import ZoraCreator1155Attribution from "../out/ZoraCreator1155Attribution.sol/ZoraCreator1155Attribution.json"; -import zoraCreator1155PremintExecutor from "../out/ZoraCreator1155PremintExecutorImpl.sol/ZoraCreator1155PremintExecutorImpl.json"; -import zoraCreator1155Impl from "../out/ZoraCreator1155Impl.sol/ZoraCreator1155Impl.json"; -import zoraCreator1155FactoryImpl from "../out/ZoraCreator1155FactoryImpl.sol/ZoraCreator1155FactoryImpl.json"; -import zoraCreatorFixedPriceSaleStrategy from "../out/ZoraCreatorFixedPriceSaleStrategy.sol/ZoraCreatorFixedPriceSaleStrategy.json"; -import protocolRewards from "../out/ProtocolRewards.sol/ProtocolRewards.json"; + import { ContractCreationConfig, PremintConfig, @@ -50,15 +50,9 @@ const publicClient = createPublicClient({ type Address = `0x${string}`; -const zeroAddress: Address = "0x0000000000000000000000000000000000000000"; - // JSON-RPC Account -const [ - deployerAccount, - creatorAccount, - collectorAccount, - mintFeeRecipientAccount, -] = (await walletClient.getAddresses()) as [Address, Address, Address, Address]; +const [deployerAccount, creatorAccount, collectorAccount] = + (await walletClient.getAddresses()) as [Address, Address, Address, Address]; type TestContext = { preminterAddress: `0x${string}`; @@ -68,81 +62,6 @@ type TestContext = { fixedPriceMinterAddress: Address; }; -const deployContractAndGetAddress = async ( - args: Parameters[0] -) => { - const hash = await walletClient.deployContract(args); - return ( - await publicClient.waitForTransactionReceipt({ - hash, - }) - ).contractAddress!; -}; - -export const deployFactoryProxy = async () => { - console.log("deploying protocol rewards"); - const protocolRewardsAddress = await deployContractAndGetAddress({ - abi: protocolRewards.abi, - bytecode: protocolRewards.bytecode.object as `0x${string}`, - account: deployerAccount, - args: [], - }); - - console.log("deploying attribution lib"); - const attributionAddress = await deployContractAndGetAddress({ - abi: ZoraCreator1155Attribution.abi, - bytecode: ZoraCreator1155Attribution.bytecode.object as `0x${string}`, - account: deployerAccount, - }); - - console.log("attribution address is ", attributionAddress); - - console.log("deploying 1155"); - const zora1155Address = await deployContractAndGetAddress({ - abi: zoraCreator1155Impl.abi, - bytecode: zoraCreator1155Impl.bytecode.object as `0x${string}`, - account: deployerAccount, - args: [0n, mintFeeRecipientAccount, zeroAddress, protocolRewardsAddress], - }); - - console.log("deploying fixed priced minter"); - const fixedPriceMinterAddress = await deployContractAndGetAddress({ - abi: zoraCreatorFixedPriceSaleStrategy.abi, - bytecode: zoraCreatorFixedPriceSaleStrategy.bytecode - .object as `0x${string}`, - account: deployerAccount, - }); - - console.log("deploying factory impl"); - const factoryImplAddress = await deployContractAndGetAddress({ - abi: zoraCreator1155FactoryImpl.abi, - bytecode: zoraCreator1155FactoryImpl.bytecode.object as `0x${string}`, - account: deployerAccount, - args: [zora1155Address, zeroAddress, fixedPriceMinterAddress, zeroAddress], - }); - - const factoryProxyAddress = factoryImplAddress!; - - return { factoryProxyAddress, zora1155Address, fixedPriceMinterAddress }; -}; - -export const deployPreminterContract = async (factoryProxyAddress: Address) => { - const deployPreminterHash = await walletClient.deployContract({ - abi: zoraCreator1155PremintExecutor.abi, - bytecode: zoraCreator1155PremintExecutor.bytecode.object as `0x${string}`, - account: deployerAccount, - args: [factoryProxyAddress], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ - hash: deployPreminterHash, - }); - - const preminterAddress = receipt.contractAddress!; - - return { preminterAddress, factoryProxyAddress }; -}; - // create token and contract creation config: const defaultContractConfig = ({ contractAdmin, @@ -176,8 +95,6 @@ const defaultPremintConfig = (fixedPriceMinter: Address): PremintConfig => ({ version: 0, }); -const useForkContract = true; - describe("ZoraCreator1155Preminter", () => { beforeEach(async (ctx) => { // deploy signature minter contract @@ -186,35 +103,22 @@ describe("ZoraCreator1155Preminter", () => { value: parseEther("10"), }); - ctx.forkedChainId = zoraTestnet.id; + ctx.forkedChainId = zora.id; ctx.anvilChainId = foundry.id; - let preminterAddress: Address; - - if (useForkContract) { - const factoryProxyAddress = - zoraCreator1155FactoryImplAddress[ctx.forkedChainId]; - ctx.fixedPriceMinterAddress = await publicClient.readContract({ - abi: zoraCreator1155FactoryImplConfig.abi, - address: zoraCreator1155FactoryImplAddress[ctx.forkedChainId], - functionName: "fixedPriceMinter", - }); - const deployed = await deployPreminterContract(factoryProxyAddress); - preminterAddress = deployed.preminterAddress; - } else { - const factoryProxyAddress = (await deployFactoryProxy()) - .factoryProxyAddress; - const deployed = await deployPreminterContract(factoryProxyAddress); - preminterAddress = deployed.preminterAddress; - } - + ctx.fixedPriceMinterAddress = await publicClient.readContract({ + abi: zoraCreator1155FactoryImplConfig.abi, + address: zoraCreator1155FactoryImplAddress[ctx.forkedChainId], + functionName: "fixedPriceMinter", + }); ctx.zoraMintFee = parseEther("0.000777"); - ctx.preminterAddress = preminterAddress; + ctx.preminterAddress = + zoraCreator1155PremintExecutorAddress[ctx.forkedChainId]; }, 20 * 1000); // skip for now - we need to make this work on zora testnet chain too - it.skip( + it( "can sign on the forked premint contract", async ({ fixedPriceMinterAddress, forkedChainId }) => { const premintConfig = defaultPremintConfig(fixedPriceMinterAddress); @@ -474,4 +378,129 @@ describe("ZoraCreator1155Preminter", () => { // 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/yarn.lock b/yarn.lock index aa89b14c2..81dc7ae64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2468,6 +2468,11 @@ isomorphic-ws@5.0.0: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== +isows@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" + integrity sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg== + jackspeak@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" @@ -4090,7 +4095,7 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -viem@^1.0.0, viem@^1.6.0: +viem@^1.0.0: version "1.14.0" resolved "https://registry.yarnpkg.com/viem/-/viem-1.14.0.tgz#e4b305c4cce500e04a66b951c01856d7b04ab403" integrity sha512-4d+4/H3lnbkSAbrpQ15i1nBA7hne06joLFy3L3m0ZpMc+g+Zr3D4nuSTyeiqbHAYs9m2P9Kjap0HlyGkehasgg== @@ -4105,6 +4110,20 @@ viem@^1.0.0, viem@^1.6.0: isomorphic-ws "5.0.0" ws "8.13.0" +viem@^1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/viem/-/viem-1.16.2.tgz#7e9719dd19e7464284b94d9c00f94f86f5858ccd" + integrity sha512-ZQ8kemNvRVwucwcsj4/SjKohK+wZv9Vxx/gXAlwqGMCW7r+niOeECtFku/1L7UPTmPgdmq4kic9R71t6XQDmGw== + dependencies: + "@adraffy/ens-normalize" "1.9.4" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@scure/bip32" "1.3.2" + "@scure/bip39" "1.2.1" + abitype "0.9.8" + isows "1.0.3" + ws "8.13.0" + vite-node@0.30.1: version "0.30.1" resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.30.1.tgz#ab0ed1553019c7d81ac95529c57ab8ac9e82347d"