@zoralabs/protocol-sdk 0.3.5 → 0.4.1

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.
@@ -9,19 +9,22 @@ import {
9
9
  zoraCreator1155FactoryImplConfig,
10
10
  } from "@zoralabs/protocol-deployments";
11
11
 
12
+ import {
13
+ premintTypedDataDefinition,
14
+ isValidSignature,
15
+ recoverCreatorFromCreatorAttribution,
16
+ getPremintExecutorAddress,
17
+ getPremintMintCosts,
18
+ } from "./preminter";
12
19
  import {
13
20
  ContractCreationConfig,
14
21
  PremintConfigV1,
15
22
  TokenCreationConfigV1,
16
- premintTypedDataDefinition,
17
- isValidSignature,
18
23
  PremintConfigVersion,
19
24
  TokenCreationConfigV2,
20
25
  PremintConfigV2,
21
26
  MintArguments,
22
- recoverCreatorFromCreatorAttribution,
23
- getPremintExecutorAddress,
24
- } from "./preminter";
27
+ } from "./contract-types";
25
28
  import { AnvilViemClientsTest, forkUrls, makeAnvilTest } from "src/anvil";
26
29
 
27
30
  // create token and contract creation config:
@@ -100,12 +103,10 @@ const defaultPremintConfigV2 = ({
100
103
  version: 0,
101
104
  });
102
105
 
103
- const ZORA_MINT_FEE = parseEther("0.000777");
104
-
105
106
  const PREMINTER_ADDRESS = getPremintExecutorAddress();
106
107
 
107
108
  const anvilTest = makeAnvilTest({
108
- forkUrl: forkUrls.zoraSepoli,
109
+ forkUrl: forkUrls.zoraSepolia,
109
110
  forkBlockNumber: 1265490,
110
111
  });
111
112
 
@@ -138,6 +139,11 @@ async function setupContracts({
138
139
  };
139
140
  }
140
141
 
142
+ const zoraSepoliaAnvilTest = makeAnvilTest({
143
+ forkUrl: forkUrls.zoraSepolia,
144
+ forkBlockNumber: 1905837,
145
+ });
146
+
141
147
  describe("ZoraCreator1155Preminter", () => {
142
148
  // skip for now - we need to make this work on zora testnet chain too
143
149
  anvilTest(
@@ -184,7 +190,7 @@ describe("ZoraCreator1155Preminter", () => {
184
190
  },
185
191
  20 * 1000,
186
192
  );
187
- anvilTest(
193
+ zoraSepoliaAnvilTest(
188
194
  "can sign and recover a v1 premint config signature",
189
195
  async ({ viemClients }) => {
190
196
  const {
@@ -237,10 +243,7 @@ describe("ZoraCreator1155Preminter", () => {
237
243
 
238
244
  20 * 1000,
239
245
  );
240
- makeAnvilTest({
241
- forkUrl: forkUrls.zoraSepoli,
242
- forkBlockNumber: 1262991,
243
- })(
246
+ zoraSepoliaAnvilTest(
244
247
  "can sign and recover a v2 premint config signature",
245
248
  async ({ viemClients }) => {
246
249
  const {
@@ -294,7 +297,7 @@ describe("ZoraCreator1155Preminter", () => {
294
297
 
295
298
  20 * 1000,
296
299
  );
297
- anvilTest(
300
+ zoraSepoliaAnvilTest(
298
301
  "can sign and mint multiple tokens",
299
302
  async ({ viemClients }) => {
300
303
  const {
@@ -335,9 +338,14 @@ describe("ZoraCreator1155Preminter", () => {
335
338
 
336
339
  const quantityToMint = 2n;
337
340
 
338
- const valueToSend =
339
- (ZORA_MINT_FEE + premintConfig1.tokenConfig.pricePerToken) *
340
- quantityToMint;
341
+ const valueToSend = (
342
+ await getPremintMintCosts({
343
+ publicClient: viemClients.publicClient,
344
+ quantityToMint,
345
+ tokenContract: contractAddress,
346
+ tokenPrice: premintConfig1.tokenConfig.pricePerToken,
347
+ })
348
+ ).totalCost;
341
349
 
342
350
  await viemClients.testClient.setBalance({
343
351
  address: collectorAccount,
@@ -359,7 +367,7 @@ describe("ZoraCreator1155Preminter", () => {
359
367
  const mintArguments: MintArguments = {
360
368
  mintComment: "",
361
369
  mintRecipient: collectorAccount,
362
- mintReferral: collectorAccount,
370
+ mintRewardsRecipients: [],
363
371
  };
364
372
 
365
373
  // now have the collector execute the first signed message;
@@ -431,9 +439,14 @@ describe("ZoraCreator1155Preminter", () => {
431
439
 
432
440
  const quantityToMint2 = 4n;
433
441
 
434
- const valueToSend2 =
435
- (ZORA_MINT_FEE + premintConfig2.tokenConfig.pricePerToken) *
436
- quantityToMint2;
442
+ const valueToSend2 = (
443
+ await getPremintMintCosts({
444
+ publicClient: viemClients.publicClient,
445
+ quantityToMint: quantityToMint2,
446
+ tokenContract: contractAddress,
447
+ tokenPrice: premintConfig2.tokenConfig.pricePerToken,
448
+ })
449
+ ).totalCost;
437
450
 
438
451
  const simulationResult = await viemClients.publicClient.simulateContract({
439
452
  abi: preminterAbi,
@@ -488,7 +501,7 @@ describe("ZoraCreator1155Preminter", () => {
488
501
  40 * 1000,
489
502
  );
490
503
 
491
- anvilTest(
504
+ zoraSepoliaAnvilTest(
492
505
  "can decode the CreatorAttribution event",
493
506
  async ({ viemClients }) => {
494
507
  const {
@@ -530,9 +543,14 @@ describe("ZoraCreator1155Preminter", () => {
530
543
 
531
544
  const quantityToMint = 2n;
532
545
 
533
- const valueToSend =
534
- (ZORA_MINT_FEE + premintConfig.tokenConfig.pricePerToken) *
535
- quantityToMint;
546
+ const valueToSend = (
547
+ await getPremintMintCosts({
548
+ publicClient: viemClients.publicClient,
549
+ quantityToMint,
550
+ tokenContract: contractAddress,
551
+ tokenPrice: premintConfig.tokenConfig.pricePerToken,
552
+ })
553
+ ).totalCost;
536
554
 
537
555
  await viemClients.testClient.setBalance({
538
556
  address: collectorAccount,
@@ -558,7 +576,7 @@ describe("ZoraCreator1155Preminter", () => {
558
576
  {
559
577
  mintComment: "",
560
578
  mintRecipient: collectorAccount,
561
- mintReferral: zeroAddress,
579
+ mintRewardsRecipients: [],
562
580
  },
563
581
  ],
564
582
  value: valueToSend,
@@ -1,5 +1,4 @@
1
1
  import { Address } from "abitype";
2
- import { ExtractAbiFunction, AbiParametersToPrimitiveTypes } from "abitype";
3
2
  import {
4
3
  zoraCreator1155PremintExecutorImplABI as preminterAbi,
5
4
  zoraCreator1155ImplABI,
@@ -17,103 +16,22 @@ import {
17
16
  concat,
18
17
  recoverAddress,
19
18
  GetEventArgs,
19
+ parseEther,
20
20
  } from "viem";
21
-
22
- type PremintV1Inputs = ExtractAbiFunction<
23
- typeof preminterAbi,
24
- "premintV1"
25
- >["inputs"];
26
-
27
- type PremintV1HashDataTypes = AbiParametersToPrimitiveTypes<PremintV1Inputs>;
28
-
29
- export type ContractCreationConfig = PremintV1HashDataTypes[0];
30
-
31
- export type PremintConfigV1 = PremintV1HashDataTypes[1];
32
- export type TokenCreationConfigV1 = PremintConfigV1["tokenConfig"];
33
-
34
- export type MintArguments = PremintV1HashDataTypes[4];
35
-
36
- type PremintV2Inputs = ExtractAbiFunction<
37
- typeof preminterAbi,
38
- "premintV2"
39
- >["inputs"];
40
-
41
- type PremintV2HashDataTypes = AbiParametersToPrimitiveTypes<PremintV2Inputs>;
42
-
43
- export type PremintConfigV2 = PremintV2HashDataTypes[1];
44
- export type TokenCreationConfigV2 = PremintConfigV2["tokenConfig"];
45
-
46
- const v1Types = {
47
- CreatorAttribution: [
48
- { name: "tokenConfig", type: "TokenCreationConfig" },
49
- // unique id scoped to the contract and token to create.
50
- // ensure that a signature can be replaced, as long as the replacement
51
- // has the same uid, and a newer version.
52
- { name: "uid", type: "uint32" },
53
- { name: "version", type: "uint32" },
54
- // if this update should result in the signature being deleted.
55
- { name: "deleted", type: "bool" },
56
- ],
57
- TokenCreationConfig: [
58
- { name: "tokenURI", type: "string" },
59
- { name: "maxSupply", type: "uint256" },
60
- { name: "maxTokensPerAddress", type: "uint64" },
61
- { name: "pricePerToken", type: "uint96" },
62
- { name: "mintStart", type: "uint64" },
63
- { name: "mintDuration", type: "uint64" },
64
- { name: "royaltyMintSchedule", type: "uint32" },
65
- { name: "royaltyBPS", type: "uint32" },
66
- { name: "royaltyRecipient", type: "address" },
67
- { name: "fixedPriceMinter", type: "address" },
68
- ],
69
- } as const;
70
-
71
- const v2Types = {
72
- CreatorAttribution: [
73
- { name: "tokenConfig", type: "TokenCreationConfig" },
74
- // unique id scoped to the contract and token to create.
75
- // ensure that a signature can be replaced, as long as the replacement
76
- // has the same uid, and a newer version.
77
- { name: "uid", type: "uint32" },
78
- { name: "version", type: "uint32" },
79
- // if this update should result in the signature being deleted.
80
- { name: "deleted", type: "bool" },
81
- ],
82
- TokenCreationConfig: [
83
- { name: "tokenURI", type: "string" },
84
- { name: "maxSupply", type: "uint256" },
85
- { name: "maxTokensPerAddress", type: "uint64" },
86
- { name: "pricePerToken", type: "uint96" },
87
- { name: "mintStart", type: "uint64" },
88
- { name: "mintDuration", type: "uint64" },
89
- { name: "royaltyBPS", type: "uint32" },
90
- { name: "payoutRecipient", type: "address" },
91
- { name: "fixedPriceMinter", type: "address" },
92
- { name: "createReferral", type: "address" },
93
- ],
94
- } as const;
95
-
96
- export const PreminterDomain = "Preminter";
97
-
98
- type PremintConfigVersion = "1" | "2";
99
-
100
- export const PremintConfigVersion = {
101
- V1: "1",
102
- V2: "2",
103
- } as const;
104
-
105
- type PremintConfigForVersion<T extends PremintConfigVersion> = T extends "1"
106
- ? PremintConfigV1
107
- : PremintConfigV2;
108
-
109
- type PremintConfigWithVersion<T extends PremintConfigVersion> = {
110
- premintConfig: PremintConfigForVersion<T>;
111
- premintConfigVersion: T;
112
- };
113
-
114
- export type PremintConfigAndVersion =
115
- | PremintConfigWithVersion<"1">
116
- | PremintConfigWithVersion<"2">;
21
+ import {
22
+ ContractCreationConfig,
23
+ PremintConfig,
24
+ PremintConfigForTokenCreationConfig,
25
+ PremintConfigV1,
26
+ PremintConfigV2,
27
+ PremintConfigVersion,
28
+ PremintConfigWithVersion,
29
+ PreminterDomain,
30
+ TokenCreationConfig,
31
+ v1Types,
32
+ v2Types,
33
+ } from "./contract-types";
34
+ import { MintCosts } from "src/mint/mint-client";
117
35
 
118
36
  export const getPremintExecutorAddress = () =>
119
37
  zoraCreator1155PremintExecutorImplAddress[999];
@@ -127,7 +45,7 @@ export const getPremintExecutorAddress = () =>
127
45
  * @param params.premintConfig the premint config
128
46
  * @returns
129
47
  */
130
- export const premintTypedDataDefinition = ({
48
+ export const premintTypedDataDefinition = <T extends PremintConfigVersion>({
131
49
  verifyingContract,
132
50
  chainId,
133
51
  premintConfigVersion: version,
@@ -135,7 +53,7 @@ export const premintTypedDataDefinition = ({
135
53
  }: {
136
54
  verifyingContract: Address;
137
55
  chainId: number;
138
- } & PremintConfigAndVersion): TypedDataDefinition => {
56
+ } & PremintConfigWithVersion<T>): TypedDataDefinition => {
139
57
  const domain = {
140
58
  chainId,
141
59
  name: PreminterDomain,
@@ -147,14 +65,14 @@ export const premintTypedDataDefinition = ({
147
65
  return {
148
66
  domain,
149
67
  types: v1Types,
150
- message: premintConfig,
68
+ message: premintConfig as PremintConfigV1,
151
69
  primaryType: "CreatorAttribution",
152
70
  } satisfies TypedDataDefinition<typeof v1Types, "CreatorAttribution">;
153
71
  if (version === PremintConfigVersion.V2) {
154
72
  return {
155
73
  domain,
156
74
  types: v2Types,
157
- message: premintConfig,
75
+ message: premintConfig as PremintConfigV2,
158
76
  primaryType: "CreatorAttribution",
159
77
  } satisfies TypedDataDefinition<typeof v2Types, "CreatorAttribution">;
160
78
  }
@@ -167,7 +85,9 @@ export type IsValidSignatureReturn = {
167
85
  recoveredAddress?: Address;
168
86
  };
169
87
 
170
- export async function isAuthorizedToCreatePremint({
88
+ export async function isAuthorizedToCreatePremint<
89
+ T extends PremintConfigVersion,
90
+ >({
171
91
  collection,
172
92
  collectionAddress,
173
93
  publicClient,
@@ -181,7 +101,7 @@ export async function isAuthorizedToCreatePremint({
181
101
  publicClient: PublicClient;
182
102
  signature: Hex;
183
103
  signer: Address;
184
- } & PremintConfigAndVersion) {
104
+ } & PremintConfigWithVersion<T>) {
185
105
  // if we are using legacy version of premint config, we can use the function
186
106
  // "isValidSignature" which we know exists on the premint executor contract
187
107
  if (premintConfigVersion === PremintConfigVersion.V1) {
@@ -189,7 +109,7 @@ export async function isAuthorizedToCreatePremint({
189
109
  abi: zoraCreator1155PremintExecutorImplABI,
190
110
  address: getPremintExecutorAddress(),
191
111
  functionName: "isValidSignature",
192
- args: [collection, premintConfig, signature],
112
+ args: [collection, premintConfig as PremintConfigV1, signature],
193
113
  });
194
114
 
195
115
  return isValidSignature;
@@ -204,14 +124,14 @@ export async function isAuthorizedToCreatePremint({
204
124
  });
205
125
  }
206
126
 
207
- export async function recoverPremintSigner({
127
+ export async function recoverPremintSigner<T extends PremintConfigVersion>({
208
128
  signature,
209
129
  ...rest
210
130
  }: {
211
131
  signature: Hex;
212
132
  chainId: number;
213
133
  verifyingContract: Address;
214
- } & PremintConfigAndVersion): Promise<Address> {
134
+ } & PremintConfigWithVersion<T>): Promise<Address> {
215
135
  const typedData = premintTypedDataDefinition(rest);
216
136
  return await recoverTypedDataAddress({
217
137
  ...typedData,
@@ -241,7 +161,7 @@ export async function tryRecoverPremintSigner(
241
161
  * @param params.tokenContract the address of the 1155 contract
242
162
  * @returns
243
163
  */
244
- export async function isValidSignature({
164
+ export async function isValidSignature<T extends PremintConfigVersion>({
245
165
  signature,
246
166
  publicClient,
247
167
  collection,
@@ -252,7 +172,7 @@ export async function isValidSignature({
252
172
  signature: Hex;
253
173
  chainId: number;
254
174
  publicClient: PublicClient;
255
- } & PremintConfigAndVersion): Promise<IsValidSignatureReturn> {
175
+ } & PremintConfigWithVersion<T>): Promise<IsValidSignatureReturn> {
256
176
  const tokenContract = await getPremintCollectionAddress({
257
177
  collection,
258
178
  publicClient,
@@ -375,22 +295,41 @@ export const recoverCreatorFromCreatorAttribution = async ({
375
295
  });
376
296
  };
377
297
 
298
+ export const supportedPremintVersions = async ({
299
+ tokenContract,
300
+ publicClient,
301
+ }: {
302
+ tokenContract: Address;
303
+ publicClient: PublicClient;
304
+ }): Promise<readonly string[]> => {
305
+ try {
306
+ return await publicClient.readContract({
307
+ abi: preminterAbi,
308
+ address: getPremintExecutorAddress(),
309
+ functionName: "supportedPremintSignatureVersions",
310
+ args: [tokenContract],
311
+ });
312
+ } catch (e) {
313
+ console.error(e);
314
+ // if the premint executor contract doesn't support the function, it must be v1
315
+ return ["1"];
316
+ }
317
+ };
378
318
  /**
379
319
  * Checks if the 1155 contract at that address supports the given version of the premint config.
380
320
  */
381
- export const supportsPremintVersion = async (
382
- version: PremintConfigVersion,
383
- tokenContract: Address,
384
- publicClient: PublicClient,
385
- ): Promise<boolean> => {
386
- const supportedPremintSignatureVersions = await publicClient.readContract({
387
- abi: preminterAbi,
388
- address: getPremintExecutorAddress(),
389
- functionName: "supportedPremintSignatureVersions",
390
- args: [tokenContract],
391
- });
392
-
393
- return supportedPremintSignatureVersions.includes(version);
321
+ export const supportsPremintVersion = async ({
322
+ version,
323
+ tokenContract,
324
+ publicClient,
325
+ }: {
326
+ version: PremintConfigVersion;
327
+ tokenContract: Address;
328
+ publicClient: PublicClient;
329
+ }): Promise<boolean> => {
330
+ return (
331
+ await supportedPremintVersions({ tokenContract, publicClient })
332
+ ).includes(version);
394
333
  };
395
334
 
396
335
  export async function getPremintCollectionAddress({
@@ -407,3 +346,105 @@ export async function getPremintCollectionAddress({
407
346
  args: [collection],
408
347
  });
409
348
  }
349
+
350
+ export function markPremintDeleted<T extends PremintConfig>(
351
+ premintConfig: T,
352
+ ): T {
353
+ return {
354
+ ...premintConfig,
355
+ version: premintConfig.version + 1,
356
+ deleted: true,
357
+ };
358
+ }
359
+
360
+ export function applyUpdateToPremint({
361
+ uid,
362
+ version,
363
+ tokenConfig,
364
+ tokenConfigUpdates,
365
+ }: {
366
+ tokenConfig: TokenCreationConfig;
367
+ tokenConfigUpdates: Partial<TokenCreationConfig>;
368
+ } & Pick<PremintConfig, "uid" | "version">): PremintConfig {
369
+ const updatedTokenConfig: TokenCreationConfig = {
370
+ ...tokenConfig,
371
+ ...tokenConfigUpdates,
372
+ } as const;
373
+
374
+ const result = {
375
+ deleted: false,
376
+ uid,
377
+ version: version + 1,
378
+ tokenConfig: updatedTokenConfig,
379
+ } as PremintConfig;
380
+
381
+ return result;
382
+ }
383
+
384
+ export function makeNewPremint<T extends TokenCreationConfig>({
385
+ tokenConfig,
386
+ uid,
387
+ }: {
388
+ tokenConfig: T;
389
+ uid: number;
390
+ }): PremintConfigForTokenCreationConfig<T> {
391
+ return {
392
+ deleted: false,
393
+ uid,
394
+ version: 0,
395
+ tokenConfig,
396
+ } as PremintConfigForTokenCreationConfig<T>;
397
+ }
398
+
399
+ export async function getPremintMintFee({
400
+ tokenContract,
401
+ publicClient,
402
+ }: {
403
+ tokenContract: Address;
404
+ publicClient: PublicClient;
405
+ }) {
406
+ // try reading mint fee function from premint executor. this will revert
407
+ // if the abi is not up to date yet
408
+ try {
409
+ return await publicClient.readContract({
410
+ address: getPremintExecutorAddress(),
411
+ abi: zoraCreator1155PremintExecutorImplABI,
412
+ functionName: "mintFee",
413
+ args: [tokenContract],
414
+ });
415
+ } catch (e) {
416
+ console.error(e);
417
+
418
+ return parseEther("0.000777");
419
+ }
420
+ }
421
+
422
+ export async function getPremintMintCosts({
423
+ publicClient,
424
+ tokenContract,
425
+ tokenPrice,
426
+ quantityToMint,
427
+ }: {
428
+ tokenContract: Address;
429
+ tokenPrice: bigint;
430
+ quantityToMint: bigint;
431
+ publicClient: PublicClient;
432
+ }): Promise<MintCosts> {
433
+ const mintFee = await getPremintMintFee({ tokenContract, publicClient });
434
+
435
+ return {
436
+ mintFee: mintFee * quantityToMint,
437
+ tokenPurchaseCost: tokenPrice * quantityToMint,
438
+ totalCost: (mintFee + tokenPrice) * quantityToMint,
439
+ };
440
+ }
441
+
442
+ export function makeMintRewardsRecipient({
443
+ mintReferral = zeroAddress,
444
+ platformReferral = zeroAddress,
445
+ }: {
446
+ mintReferral?: Address;
447
+ platformReferral?: Address;
448
+ }): Address[] {
449
+ return [mintReferral, platformReferral];
450
+ }