@zoralabs/protocol-sdk 0.5.15 → 0.5.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +19 -0
  3. package/dist/anvil.d.ts +4 -4
  4. package/dist/anvil.d.ts.map +1 -1
  5. package/dist/apis/generated/premint-api-types.d.ts +9 -2
  6. package/dist/apis/generated/premint-api-types.d.ts.map +1 -1
  7. package/dist/create/1155-create-helper.d.ts +3 -4
  8. package/dist/create/1155-create-helper.d.ts.map +1 -1
  9. package/dist/index.cjs +495 -371
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +478 -352
  14. package/dist/index.js.map +1 -1
  15. package/dist/mint/mint-client.d.ts +22 -17
  16. package/dist/mint/mint-client.d.ts.map +1 -1
  17. package/dist/mints/mints-contracts.d.ts +688 -3
  18. package/dist/mints/mints-contracts.d.ts.map +1 -1
  19. package/dist/premint/contract-types.d.ts +21 -0
  20. package/dist/premint/contract-types.d.ts.map +1 -1
  21. package/dist/premint/conversions.d.ts +10 -22
  22. package/dist/premint/conversions.d.ts.map +1 -1
  23. package/dist/premint/premint-api-client.d.ts +5 -4
  24. package/dist/premint/premint-api-client.d.ts.map +1 -1
  25. package/dist/premint/premint-client.d.ts +122 -1476
  26. package/dist/premint/premint-client.d.ts.map +1 -1
  27. package/dist/premint/preminter.d.ts +29 -16
  28. package/dist/premint/preminter.d.ts.map +1 -1
  29. package/dist/utils.d.ts +6872 -1
  30. package/dist/utils.d.ts.map +1 -1
  31. package/package.json +2 -2
  32. package/src/apis/generated/premint-api-types.ts +9 -2
  33. package/src/create/1155-create-helper.test.ts +10 -3
  34. package/src/create/1155-create-helper.ts +8 -7
  35. package/src/index.ts +6 -0
  36. package/src/mint/mint-client.ts +31 -30
  37. package/src/mints/mints-contracts.test.ts +2 -2
  38. package/src/premint/contract-types.ts +32 -1
  39. package/src/premint/conversions.ts +20 -8
  40. package/src/premint/premint-api-client.ts +7 -7
  41. package/src/premint/premint-client.test.ts +112 -88
  42. package/src/premint/premint-client.ts +614 -409
  43. package/src/premint/preminter.test.ts +154 -2
  44. package/src/premint/preminter.ts +87 -36
  45. package/src/utils.ts +25 -0
  46. package/test-integration/premint-client.test.ts +2 -2
@@ -1,4 +1,11 @@
1
- import { Address, zeroAddress } from "viem";
1
+ import {
2
+ Address,
3
+ Hex,
4
+ hashTypedData,
5
+ keccak256,
6
+ stringToBytes,
7
+ zeroAddress,
8
+ } from "viem";
2
9
  import { zoraSepolia } from "viem/chains";
3
10
  import { describe, expect } from "vitest";
4
11
  import { parseEther } from "viem";
@@ -24,6 +31,20 @@ import {
24
31
  getPremintMintCosts,
25
32
  } from "./preminter";
26
33
  import { AnvilViemClientsTest, forkUrls, makeAnvilTest } from "src/anvil";
34
+ import { privateKeyToAccount } from "viem/accounts";
35
+
36
+ const erc1271Abi = [
37
+ {
38
+ type: "function",
39
+ name: "isValidSignature",
40
+ inputs: [
41
+ { name: "_hash", type: "bytes32", internalType: "bytes32" },
42
+ { name: "_signature", type: "bytes", internalType: "bytes" },
43
+ ],
44
+ outputs: [{ name: "", type: "bytes4", internalType: "bytes4" }],
45
+ stateMutability: "view",
46
+ },
47
+ ] as const;
27
48
 
28
49
  // create token and contract creation config:
29
50
  export const defaultContractConfig = ({
@@ -152,7 +173,7 @@ async function setupContracts({
152
173
 
153
174
  const zoraSepoliaAnvilTest = makeAnvilTest({
154
175
  forkUrl: forkUrls.zoraSepolia,
155
- forkBlockNumber: 8948974,
176
+ forkBlockNumber: 9467979,
156
177
  anvilChainId: zoraSepolia.id,
157
178
  });
158
179
 
@@ -239,6 +260,7 @@ describe("ZoraCreator1155Preminter", () => {
239
260
  // recover and verify address is correct
240
261
  const { recoveredAddress, isAuthorized } = await isValidSignature({
241
262
  collection: contractConfig,
263
+ collectionAddress: tokenContract,
242
264
  chainId: viemClients.publicClient.chain!.id,
243
265
  premintConfig,
244
266
  premintConfigVersion: PremintConfigVersion.V1,
@@ -293,6 +315,7 @@ describe("ZoraCreator1155Preminter", () => {
293
315
  // recover and verify address is correct
294
316
  const { recoveredAddress, isAuthorized } = await isValidSignature({
295
317
  collection: contractConfig,
318
+ collectionAddress: tokenContract,
296
319
  chainId: viemClients.publicClient.chain!.id,
297
320
  premintConfig,
298
321
  premintConfigVersion: PremintConfigVersion.V2,
@@ -524,6 +547,135 @@ describe("ZoraCreator1155Preminter", () => {
524
547
  },
525
548
  40 * 1000,
526
549
  ),
550
+ zoraSepoliaAnvilTest(
551
+ "can sign a premint from an smart contract account and mint tokens against that premint",
552
+ async ({ viemClients }) => {
553
+ const {
554
+ fixedPriceMinterAddress,
555
+ accounts: { collectorAccount },
556
+ } = await setupContracts({ viemClients });
557
+
558
+ // this test shows how to create a premint that is signed by an EOA, but the premint
559
+ // contract admin will be a smart wallet owned by the EOA.
560
+ // contract admin is set as the smart wallet. Smart wallet owner is the EOA.
561
+ // EOA signs the premint.
562
+ // When calling `premint` smart wallets address must be passed as an argument
563
+
564
+ // this was an AA contract that was deployed that has has the owner below as the
565
+ // valid signer. See https://sepolia.explorer.zora.energy/address/0x74F5fAf983d54FEd6D937654Aa4FD258534F2d4B?tab=contract
566
+ // it was deployed via the script `packages/1155-deployments/script/DeploySimpleAA.s.sol`
567
+ const smartWalletAddress = "0x74F5fAf983d54FEd6D937654Aa4FD258534F2d4B";
568
+ const ownerAddress = "0x7c8999dC9a822c1f0Df42023113EDB4FDd543266";
569
+ const ownerPrivateKey =
570
+ "0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0";
571
+
572
+ const ownerAccount = privateKeyToAccount(ownerPrivateKey);
573
+
574
+ const premintConfig = defaultPremintConfigV2({
575
+ fixedPriceMinter: fixedPriceMinterAddress,
576
+ // we set the creator to the AA contract
577
+ creatorAccount: smartWalletAddress,
578
+ });
579
+
580
+ expect(ownerAccount.address).toBe(ownerAddress);
581
+
582
+ const contractConfig = defaultContractConfig({
583
+ // for the contract config, we set the smart wallet as the admin
584
+ contractAdmin: smartWalletAddress,
585
+ });
586
+
587
+ let contractAddress = await viemClients.publicClient.readContract({
588
+ abi: preminterAbi,
589
+ address: PREMINTER_ADDRESS,
590
+ functionName: "getContractWithAdditionalAdminsAddress",
591
+ args: [contractConfig],
592
+ });
593
+
594
+ const typedData = premintTypedDataDefinition({
595
+ verifyingContract: contractAddress,
596
+ // we need to sign here for the anvil chain, cause thats where it is run on
597
+ chainId: viemClients.chain.id,
598
+ premintConfig: premintConfig,
599
+ premintConfigVersion: PremintConfigVersion.V2,
600
+ });
601
+
602
+ // have creator sign the message to create the contract
603
+ // and the token
604
+ const signedMessage = await viemClients.walletClient.signTypedData({
605
+ ...typedData,
606
+ account: ownerAccount,
607
+ });
608
+
609
+ // sanity check - validate the signature on the smart wallet contract
610
+ const result = await viemClients.publicClient.readContract({
611
+ abi: erc1271Abi,
612
+ address: smartWalletAddress,
613
+ functionName: "isValidSignature",
614
+ args: [hashTypedData(typedData), signedMessage],
615
+ });
616
+
617
+ // if is a valid signature, signature should return `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`
618
+ const expectedMagicValue = keccak256(
619
+ stringToBytes("isValidSignature(bytes32,bytes)"),
620
+ ).slice(0, 10);
621
+
622
+ expect(result).toBe(expectedMagicValue);
623
+
624
+ const quantityToMint = 2n;
625
+
626
+ const valueToSend = (
627
+ await getPremintMintCosts({
628
+ publicClient: viemClients.publicClient,
629
+ quantityToMint,
630
+ tokenContract: contractAddress,
631
+ tokenPrice: premintConfig.tokenConfig.pricePerToken,
632
+ })
633
+ ).totalCost;
634
+
635
+ await viemClients.testClient.setBalance({
636
+ address: collectorAccount,
637
+ value: parseEther("10"),
638
+ });
639
+
640
+ const mintArguments: PremintMintArguments = {
641
+ mintComment: "",
642
+ mintRecipient: collectorAccount,
643
+ mintRewardsRecipients: [],
644
+ };
645
+
646
+ const firstMinter = collectorAccount;
647
+
648
+ // now have the collector execute the first signed message;
649
+ // it should create the contract, the token,
650
+ // and min the quantity to mint tokens to the collector
651
+ // the signature along with contract + token creation
652
+ // parameters are required to call this function
653
+ await viemClients.publicClient.simulateContract({
654
+ abi: preminterAbi,
655
+ functionName: "premint",
656
+ account: collectorAccount,
657
+ chain: viemClients.chain,
658
+ address: PREMINTER_ADDRESS,
659
+ args: [
660
+ contractConfig,
661
+ zeroAddress,
662
+ encodePremintConfig({
663
+ premintConfig: premintConfig,
664
+ premintConfigVersion: PremintConfigVersion.V2,
665
+ }),
666
+ signedMessage,
667
+ quantityToMint,
668
+ mintArguments,
669
+ firstMinter,
670
+ // we must specify the smart wallet address in the call, so that the 1155 contract
671
+ // knows to have the smart wallet validate the signature
672
+ smartWalletAddress,
673
+ ],
674
+ value: valueToSend,
675
+ });
676
+ },
677
+ 40 * 1000,
678
+ ),
527
679
  zoraSepoliaAnvilTest(
528
680
  "can have collaborators create premints that can be executed on existing contracts",
529
681
  async ({ viemClients }) => {
@@ -6,11 +6,12 @@ import {
6
6
  zoraCreator1155PremintExecutorImplAddress,
7
7
  zoraCreatorFixedPriceSaleStrategyAddress,
8
8
  premintTypedDataDefinition,
9
+ ContractCreationConfig,
10
+ PremintConfigForVersion,
9
11
  } from "@zoralabs/protocol-deployments";
10
12
  import {
11
13
  recoverTypedDataAddress,
12
14
  Hex,
13
- PublicClient,
14
15
  zeroAddress,
15
16
  hashDomain,
16
17
  keccak256,
@@ -18,11 +19,10 @@ import {
18
19
  recoverAddress,
19
20
  GetEventArgs,
20
21
  parseEther,
22
+ Account,
21
23
  } from "viem";
22
24
  import {
23
- ContractCreationConfig,
24
25
  PremintConfig,
25
- PremintConfigForTokenCreationConfig,
26
26
  PremintConfigV1,
27
27
  PremintConfigV2,
28
28
  PremintConfigVersion,
@@ -30,6 +30,12 @@ import {
30
30
  TokenCreationConfig,
31
31
  } from "@zoralabs/protocol-deployments";
32
32
  import { MintCosts } from "src/mint/mint-client";
33
+ import { PublicClient } from "src/utils";
34
+ import {
35
+ ContractCreationConfigAndAddress,
36
+ ContractCreationConfigOrAddress,
37
+ ContractCreationConfigWithOptionalAdditionalAdmins,
38
+ } from "./contract-types";
33
39
 
34
40
  export const getPremintExecutorAddress = () =>
35
41
  zoraCreator1155PremintExecutorImplAddress[999] as Address;
@@ -40,28 +46,28 @@ export type IsValidSignatureReturn = {
40
46
  };
41
47
 
42
48
  export async function isAuthorizedToCreatePremint({
43
- collection,
49
+ contractAdmin = zeroAddress,
50
+ additionalAdmins = [],
44
51
  collectionAddress,
45
52
  publicClient,
46
53
  signer,
47
54
  }: {
48
- collection: ContractCreationConfig;
55
+ contractAdmin?: Address;
56
+ additionalAdmins?: Address[];
49
57
  collectionAddress: Address;
50
58
  publicClient: PublicClient;
51
- signer: Address;
59
+ signer: Address | Account;
52
60
  }) {
53
- if (collection.additionalAdmins.length > 0)
54
- throw new Error("additionalAdmins not supported yet.");
55
61
  // otherwize, we must assume the newer version of premint executor is deployed, so we call that.
56
62
  return await publicClient.readContract({
57
63
  abi: preminterAbi,
58
64
  address: getPremintExecutorAddress(),
59
65
  functionName: "isAuthorizedToCreatePremintWithAdditionalAdmins",
60
66
  args: [
61
- signer,
62
- collection.contractAdmin,
67
+ typeof signer === "string" ? signer : signer.address,
68
+ contractAdmin,
63
69
  collectionAddress,
64
- collection.additionalAdmins,
70
+ additionalAdmins,
65
71
  ],
66
72
  });
67
73
  }
@@ -106,22 +112,19 @@ export async function isValidSignature<T extends PremintConfigVersion>({
106
112
  signature,
107
113
  publicClient,
108
114
  collection,
115
+ collectionAddress,
109
116
  chainId,
110
117
  ...premintConfigAndVersion
111
118
  }: {
112
- collection: ContractCreationConfig;
113
119
  signature: Hex;
114
120
  chainId: number;
115
121
  publicClient: PublicClient;
116
- } & PremintConfigWithVersion<T>): Promise<IsValidSignatureReturn> {
117
- const tokenContract = await getPremintCollectionAddress({
118
- collection,
119
- publicClient,
120
- });
122
+ } & PremintConfigWithVersion<T> &
123
+ ContractCreationConfigAndAddress): Promise<IsValidSignatureReturn> {
121
124
  const recoveredAddress = await tryRecoverPremintSigner({
122
125
  ...premintConfigAndVersion,
123
126
  signature,
124
- verifyingContract: tokenContract,
127
+ verifyingContract: collectionAddress,
125
128
  chainId,
126
129
  });
127
130
 
@@ -133,8 +136,9 @@ export async function isValidSignature<T extends PremintConfigVersion>({
133
136
 
134
137
  const isAuthorized = await isAuthorizedToCreatePremint({
135
138
  signer: recoveredAddress!,
136
- collection,
137
- collectionAddress: tokenContract,
139
+ additionalAdmins: collection?.additionalAdmins,
140
+ contractAdmin: collection?.contractAdmin,
141
+ collectionAddress: collectionAddress,
138
142
  publicClient,
139
143
  });
140
144
 
@@ -240,13 +244,13 @@ export const supportedPremintVersions = async ({
240
244
  }: {
241
245
  tokenContract: Address;
242
246
  publicClient: PublicClient;
243
- }): Promise<readonly string[]> => {
244
- return await publicClient.readContract({
247
+ }): Promise<PremintConfigVersion[]> => {
248
+ return (await publicClient.readContract({
245
249
  abi: preminterAbi,
246
250
  address: getPremintExecutorAddress(),
247
251
  functionName: "supportedPremintSignatureVersions",
248
252
  args: [tokenContract],
249
- });
253
+ })) as PremintConfigVersion[];
250
254
  };
251
255
  /**
252
256
  * Checks if the 1155 contract at that address supports the given version of the premint config.
@@ -266,18 +270,27 @@ export const supportsPremintVersion = async ({
266
270
  };
267
271
 
268
272
  export async function getPremintCollectionAddress({
269
- collection,
270
273
  publicClient,
274
+ collection,
275
+ collectionAddress,
271
276
  }: {
272
- collection: ContractCreationConfig;
273
277
  publicClient: PublicClient;
274
- }): Promise<Address> {
275
- return publicClient.readContract({
276
- address: getPremintExecutorAddress(),
277
- abi: zoraCreator1155PremintExecutorImplABI,
278
- functionName: "getContractAddress",
279
- args: [collection],
280
- });
278
+ } & ContractCreationConfigOrAddress): Promise<Address> {
279
+ if (typeof collection !== "undefined") {
280
+ return publicClient.readContract({
281
+ address: getPremintExecutorAddress(),
282
+ abi: zoraCreator1155PremintExecutorImplABI,
283
+ functionName: "getContractWithAdditionalAdminsAddress",
284
+ args: [
285
+ {
286
+ ...collection,
287
+ additionalAdmins: collection.additionalAdmins || [],
288
+ },
289
+ ],
290
+ });
291
+ }
292
+
293
+ return collectionAddress;
281
294
  }
282
295
 
283
296
  export function applyUpdateToPremint({
@@ -304,19 +317,19 @@ export function applyUpdateToPremint({
304
317
  return result;
305
318
  }
306
319
 
307
- export function makeNewPremint<T extends TokenCreationConfig>({
320
+ export function makeNewPremint<T extends PremintConfigVersion>({
308
321
  tokenConfig,
309
322
  uid,
310
323
  }: {
311
- tokenConfig: T;
324
+ tokenConfig: PremintConfigForVersion<T>["tokenConfig"];
312
325
  uid: number;
313
- }): PremintConfigForTokenCreationConfig<T> {
326
+ }): PremintConfigForVersion<T> {
314
327
  return {
315
328
  deleted: false,
316
329
  uid,
317
330
  version: 0,
318
331
  tokenConfig,
319
- } as PremintConfigForTokenCreationConfig<T>;
332
+ } as PremintConfigForVersion<T>;
320
333
  }
321
334
 
322
335
  export async function getPremintMintFee({
@@ -377,3 +390,41 @@ export function getDefaultFixedPriceMinterAddress(chainId: number): Address {
377
390
  chainId as keyof typeof zoraCreatorFixedPriceSaleStrategyAddress
378
391
  ]!;
379
392
  }
393
+
394
+ export const emptyContractCreationConfig = (): ContractCreationConfig => ({
395
+ contractAdmin: zeroAddress,
396
+ contractURI: "",
397
+ contractName: "",
398
+ additionalAdmins: [],
399
+ });
400
+
401
+ export function defaultAdditionalAdmins(
402
+ collection: ContractCreationConfigWithOptionalAdditionalAdmins,
403
+ ): ContractCreationConfig {
404
+ return {
405
+ ...collection,
406
+ additionalAdmins: collection.additionalAdmins || [],
407
+ };
408
+ }
409
+
410
+ export const toContractCreationConfigOrAddress = ({
411
+ collection,
412
+ collectionAddress,
413
+ }: {
414
+ collection?: ContractCreationConfigWithOptionalAdditionalAdmins;
415
+ collectionAddress?: Address;
416
+ }) => {
417
+ if (typeof collection !== "undefined") {
418
+ return {
419
+ collection,
420
+ };
421
+ }
422
+
423
+ if (typeof collectionAddress !== "undefined") {
424
+ return {
425
+ collectionAddress,
426
+ };
427
+ }
428
+
429
+ throw new Error("Must provide either a collection or a collection address");
430
+ };
package/src/utils.ts CHANGED
@@ -5,8 +5,16 @@ import {
5
5
  Chain,
6
6
  ContractFunctionArgs,
7
7
  ContractFunctionName,
8
+ PublicClient as BasePublicClient,
8
9
  SimulateContractParameters,
10
+ Transport,
11
+ createPublicClient,
12
+ http,
9
13
  } from "viem";
14
+ import {
15
+ IHttpClient,
16
+ httpClient as defaultHttpClient,
17
+ } from "./apis/http-api-base";
10
18
 
11
19
  export const makeSimulateContractParamaters = <
12
20
  const abi extends Abi | readonly unknown[],
@@ -28,3 +36,20 @@ export const makeSimulateContractParamaters = <
28
36
  accountOverride
29
37
  >,
30
38
  ) => args;
39
+
40
+ export type PublicClient = BasePublicClient<Transport, Chain>;
41
+
42
+ export type ClientConfig = {
43
+ chain: Chain;
44
+ publicClient?: PublicClient;
45
+ httpClient?: IHttpClient;
46
+ };
47
+
48
+ export function setupClient({ chain, httpClient, publicClient }: ClientConfig) {
49
+ return {
50
+ chain,
51
+ httpClient: httpClient || defaultHttpClient,
52
+ publicClient:
53
+ publicClient || createPublicClient({ chain, transport: http() }),
54
+ };
55
+ }
@@ -47,7 +47,7 @@ tests.forEach(({ anvilTest, chain }) => {
47
47
  const { uid, verifyingContract } =
48
48
  await premintClient.createPremint({
49
49
  walletClient,
50
- creatorAccount: creatorAccount!,
50
+ payoutRecipient: creatorAccount!,
51
51
  checkSignature: true,
52
52
  collection: {
53
53
  contractAdmin: creatorAccount!,
@@ -104,7 +104,7 @@ tests.forEach(({ anvilTest, chain }) => {
104
104
  const { uid, verifyingContract } =
105
105
  await premintClient.createPremint({
106
106
  walletClient,
107
- creatorAccount: creatorAccount!,
107
+ payoutRecipient: creatorAccount!,
108
108
  checkSignature: true,
109
109
  collection: {
110
110
  contractAdmin: creatorAccount!,