@zoralabs/protocol-sdk 0.3.4 → 0.3.5

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.
@@ -1,17 +1,9 @@
1
- import {
2
- keccak256,
3
- Hex,
4
- concat,
5
- recoverAddress,
6
- hashDomain,
7
- Address,
8
- } from "viem";
1
+ import { Address, zeroAddress } from "viem";
9
2
  import { foundry } from "viem/chains";
10
3
  import { describe, expect } from "vitest";
11
4
  import { parseEther } from "viem";
12
5
  import {
13
6
  zoraCreator1155PremintExecutorImplABI as preminterAbi,
14
- zoraCreator1155PremintExecutorImplAddress as zoraCreator1155PremintExecutorAddress,
15
7
  zoraCreator1155ImplABI,
16
8
  zoraCreator1155FactoryImplAddress,
17
9
  zoraCreator1155FactoryImplConfig,
@@ -19,16 +11,18 @@ import {
19
11
 
20
12
  import {
21
13
  ContractCreationConfig,
22
- PremintConfig,
23
- TokenCreationConfig,
24
- preminterTypedDataDefinition,
14
+ PremintConfigV1,
15
+ TokenCreationConfigV1,
16
+ premintTypedDataDefinition,
17
+ isValidSignature,
18
+ PremintConfigVersion,
19
+ TokenCreationConfigV2,
20
+ PremintConfigV2,
21
+ MintArguments,
22
+ recoverCreatorFromCreatorAttribution,
23
+ getPremintExecutorAddress,
25
24
  } from "./preminter";
26
- import {
27
- AnvilViemClientsTest,
28
- anvilTest,
29
- forkUrls,
30
- makeAnvilTest,
31
- } from "src/anvil";
25
+ import { AnvilViemClientsTest, forkUrls, makeAnvilTest } from "src/anvil";
32
26
 
33
27
  // create token and contract creation config:
34
28
  const defaultContractConfig = ({
@@ -41,10 +35,10 @@ const defaultContractConfig = ({
41
35
  contractName: "My fun NFT",
42
36
  });
43
37
 
44
- const defaultTokenConfig = (
38
+ const defaultTokenConfigV1 = (
45
39
  fixedPriceMinterAddress: Address,
46
40
  creatorAccount: Address,
47
- ): TokenCreationConfig => ({
41
+ ): TokenCreationConfigV1 => ({
48
42
  tokenURI: "ipfs://tokenIpfsId0",
49
43
  maxSupply: 100n,
50
44
  maxTokensPerAddress: 10n,
@@ -57,19 +51,63 @@ const defaultTokenConfig = (
57
51
  fixedPriceMinter: fixedPriceMinterAddress,
58
52
  });
59
53
 
60
- const defaultPremintConfig = (
61
- fixedPriceMinter: Address,
54
+ const defaultTokenConfigV2 = (
55
+ fixedPriceMinterAddress: Address,
62
56
  creatorAccount: Address,
63
- ): PremintConfig => ({
64
- tokenConfig: defaultTokenConfig(fixedPriceMinter, creatorAccount),
57
+ createReferral: Address,
58
+ ): TokenCreationConfigV2 => ({
59
+ tokenURI: "ipfs://tokenIpfsId0",
60
+ maxSupply: 100n,
61
+ maxTokensPerAddress: 10n,
62
+ pricePerToken: 0n,
63
+ mintStart: 0n,
64
+ mintDuration: 100n,
65
+ royaltyBPS: 200,
66
+ payoutRecipient: creatorAccount,
67
+ fixedPriceMinter: fixedPriceMinterAddress,
68
+ createReferral,
69
+ });
70
+
71
+ const defaultPremintConfigV1 = ({
72
+ fixedPriceMinter,
73
+ creatorAccount,
74
+ }: {
75
+ fixedPriceMinter: Address;
76
+ creatorAccount: Address;
77
+ }): PremintConfigV1 => ({
78
+ tokenConfig: defaultTokenConfigV1(fixedPriceMinter, creatorAccount),
65
79
  deleted: false,
66
80
  uid: 105,
67
81
  version: 0,
68
82
  });
69
83
 
84
+ const defaultPremintConfigV2 = ({
85
+ fixedPriceMinter,
86
+ creatorAccount,
87
+ createReferral = zeroAddress,
88
+ }: {
89
+ fixedPriceMinter: Address;
90
+ creatorAccount: Address;
91
+ createReferral?: Address;
92
+ }): PremintConfigV2 => ({
93
+ tokenConfig: defaultTokenConfigV2(
94
+ fixedPriceMinter,
95
+ creatorAccount,
96
+ createReferral,
97
+ ),
98
+ deleted: false,
99
+ uid: 106,
100
+ version: 0,
101
+ });
102
+
70
103
  const ZORA_MINT_FEE = parseEther("0.000777");
71
104
 
72
- const PREMINTER_ADDRESS = zoraCreator1155PremintExecutorAddress[999];
105
+ const PREMINTER_ADDRESS = getPremintExecutorAddress();
106
+
107
+ const anvilTest = makeAnvilTest({
108
+ forkUrl: forkUrls.zoraSepoli,
109
+ forkBlockNumber: 1265490,
110
+ });
73
111
 
74
112
  async function setupContracts({
75
113
  viemClients: { walletClient, testClient, publicClient },
@@ -109,15 +147,15 @@ describe("ZoraCreator1155Preminter", () => {
109
147
  fixedPriceMinterAddress,
110
148
  accounts: { creatorAccount },
111
149
  } = await setupContracts({ viemClients });
112
- const premintConfig = defaultPremintConfig(
113
- fixedPriceMinterAddress,
150
+ const premintConfig = defaultPremintConfigV1({
151
+ fixedPriceMinter: fixedPriceMinterAddress,
114
152
  creatorAccount,
115
- );
153
+ });
116
154
  const contractConfig = defaultContractConfig({
117
155
  contractAdmin: creatorAccount,
118
156
  });
119
157
 
120
- const preminterAddress = zoraCreator1155PremintExecutorAddress[999];
158
+ const preminterAddress = getPremintExecutorAddress();
121
159
 
122
160
  const contractAddress = await viemClients.publicClient.readContract({
123
161
  abi: preminterAbi,
@@ -127,10 +165,11 @@ describe("ZoraCreator1155Preminter", () => {
127
165
  });
128
166
 
129
167
  const signedMessage = await viemClients.walletClient.signTypedData({
130
- ...preminterTypedDataDefinition({
168
+ ...premintTypedDataDefinition({
131
169
  verifyingContract: contractAddress,
132
170
  chainId: 999,
133
171
  premintConfig,
172
+ premintConfigVersion: PremintConfigVersion.V1,
134
173
  }),
135
174
  account: creatorAccount,
136
175
  });
@@ -145,26 +184,80 @@ describe("ZoraCreator1155Preminter", () => {
145
184
  },
146
185
  20 * 1000,
147
186
  );
187
+ anvilTest(
188
+ "can sign and recover a v1 premint config signature",
189
+ async ({ viemClients }) => {
190
+ const {
191
+ fixedPriceMinterAddress,
192
+ accounts: { creatorAccount },
193
+ } = await setupContracts({ viemClients });
194
+
195
+ const premintConfig = defaultPremintConfigV1({
196
+ fixedPriceMinter: fixedPriceMinterAddress,
197
+ creatorAccount,
198
+ });
199
+ const contractConfig = defaultContractConfig({
200
+ contractAdmin: creatorAccount,
201
+ });
202
+
203
+ const tokenContract = await viemClients.publicClient.readContract({
204
+ abi: preminterAbi,
205
+ address: PREMINTER_ADDRESS,
206
+ functionName: "getContractAddress",
207
+ args: [contractConfig],
208
+ });
209
+
210
+ // sign message containing contract and token creation config and uid
211
+ const signedMessage = await viemClients.walletClient.signTypedData({
212
+ ...premintTypedDataDefinition({
213
+ verifyingContract: tokenContract,
214
+ // we need to sign here for the anvil chain, cause thats where it is run on
215
+ chainId: foundry.id,
216
+ premintConfig,
217
+ premintConfigVersion: PremintConfigVersion.V1,
218
+ }),
219
+ account: creatorAccount,
220
+ });
221
+
222
+ // recover and verify address is correct
223
+ const { recoveredAddress, isAuthorized } = await isValidSignature({
224
+ collection: contractConfig,
225
+ chainId: viemClients.publicClient.chain!.id,
226
+ premintConfig,
227
+ premintConfigVersion: PremintConfigVersion.V1,
228
+ publicClient: viemClients.publicClient,
229
+ signature: signedMessage,
230
+ });
231
+
232
+ expect(recoveredAddress).to.equal(creatorAccount);
233
+ expect(isAuthorized).toBe(true);
234
+
235
+ expect(recoveredAddress).to.equal(creatorAccount);
236
+ },
237
+
238
+ 20 * 1000,
239
+ );
148
240
  makeAnvilTest({
149
- forkUrl: forkUrls.zoraGoerli,
150
- forkBlockNumber: 1676105,
241
+ forkUrl: forkUrls.zoraSepoli,
242
+ forkBlockNumber: 1262991,
151
243
  })(
152
- "can sign and recover a signature",
244
+ "can sign and recover a v2 premint config signature",
153
245
  async ({ viemClients }) => {
154
246
  const {
155
247
  fixedPriceMinterAddress,
156
248
  accounts: { creatorAccount },
157
249
  } = await setupContracts({ viemClients });
158
250
 
159
- const premintConfig = defaultPremintConfig(
160
- fixedPriceMinterAddress,
251
+ const premintConfig = defaultPremintConfigV2({
161
252
  creatorAccount,
162
- );
253
+ fixedPriceMinter: fixedPriceMinterAddress,
254
+ createReferral: creatorAccount,
255
+ });
163
256
  const contractConfig = defaultContractConfig({
164
257
  contractAdmin: creatorAccount,
165
258
  });
166
259
 
167
- const contractAddress = await viemClients.publicClient.readContract({
260
+ const tokenContract = await viemClients.publicClient.readContract({
168
261
  abi: preminterAbi,
169
262
  address: PREMINTER_ADDRESS,
170
263
  functionName: "getContractAddress",
@@ -173,24 +266,28 @@ describe("ZoraCreator1155Preminter", () => {
173
266
 
174
267
  // sign message containing contract and token creation config and uid
175
268
  const signedMessage = await viemClients.walletClient.signTypedData({
176
- ...preminterTypedDataDefinition({
177
- verifyingContract: contractAddress,
269
+ ...premintTypedDataDefinition({
270
+ verifyingContract: tokenContract,
178
271
  // we need to sign here for the anvil chain, cause thats where it is run on
179
272
  chainId: foundry.id,
180
273
  premintConfig,
274
+ premintConfigVersion: PremintConfigVersion.V2,
181
275
  }),
182
276
  account: creatorAccount,
183
277
  });
184
278
 
185
- const preminterAddress = zoraCreator1155PremintExecutorAddress[999];
186
279
  // recover and verify address is correct
187
- const [, , recoveredAddress] =
188
- await viemClients.publicClient.readContract({
189
- abi: preminterAbi,
190
- address: preminterAddress,
191
- functionName: "isValidSignature",
192
- args: [contractConfig, premintConfig, signedMessage],
193
- });
280
+ const { recoveredAddress, isAuthorized } = await isValidSignature({
281
+ collection: contractConfig,
282
+ chainId: viemClients.publicClient.chain!.id,
283
+ premintConfig,
284
+ premintConfigVersion: PremintConfigVersion.V2,
285
+ publicClient: viemClients.publicClient,
286
+ signature: signedMessage,
287
+ });
288
+
289
+ expect(recoveredAddress).to.equal(creatorAccount);
290
+ expect(isAuthorized).toBe(true);
194
291
 
195
292
  expect(recoveredAddress).to.equal(creatorAccount);
196
293
  },
@@ -205,16 +302,16 @@ describe("ZoraCreator1155Preminter", () => {
205
302
  accounts: { creatorAccount, collectorAccount },
206
303
  } = await setupContracts({ viemClients });
207
304
  // setup contract and token creation parameters
208
- const premintConfig = defaultPremintConfig(
209
- fixedPriceMinterAddress,
305
+ const premintConfig1 = defaultPremintConfigV1({
306
+ fixedPriceMinter: fixedPriceMinterAddress,
210
307
  creatorAccount,
211
- );
308
+ });
212
309
  const contractConfig = defaultContractConfig({
213
310
  contractAdmin: creatorAccount,
214
311
  });
215
312
 
216
313
  // lets make it a random number to not break the existing tests that expect fresh data
217
- premintConfig.uid = Math.round(Math.random() * 1000000);
314
+ premintConfig1.uid = Math.round(Math.random() * 1000000);
218
315
 
219
316
  let contractAddress = await viemClients.publicClient.readContract({
220
317
  abi: preminterAbi,
@@ -226,11 +323,12 @@ describe("ZoraCreator1155Preminter", () => {
226
323
  // have creator sign the message to create the contract
227
324
  // and the token
228
325
  const signedMessage = await viemClients.walletClient.signTypedData({
229
- ...preminterTypedDataDefinition({
326
+ ...premintTypedDataDefinition({
230
327
  verifyingContract: contractAddress,
231
328
  // we need to sign here for the anvil chain, cause thats where it is run on
232
329
  chainId: foundry.id,
233
- premintConfig,
330
+ premintConfig: premintConfig1,
331
+ premintConfigVersion: PremintConfigVersion.V1,
234
332
  }),
235
333
  account: creatorAccount,
236
334
  });
@@ -238,11 +336,9 @@ describe("ZoraCreator1155Preminter", () => {
238
336
  const quantityToMint = 2n;
239
337
 
240
338
  const valueToSend =
241
- (ZORA_MINT_FEE + premintConfig.tokenConfig.pricePerToken) *
339
+ (ZORA_MINT_FEE + premintConfig1.tokenConfig.pricePerToken) *
242
340
  quantityToMint;
243
341
 
244
- const comment = "I love this!";
245
-
246
342
  await viemClients.testClient.setBalance({
247
343
  address: collectorAccount,
248
344
  value: parseEther("10"),
@@ -254,12 +350,18 @@ describe("ZoraCreator1155Preminter", () => {
254
350
  abi: preminterAbi,
255
351
  address: PREMINTER_ADDRESS,
256
352
  functionName: "premintStatus",
257
- args: [contractAddress, premintConfig.uid],
353
+ args: [contractAddress, premintConfig1.uid],
258
354
  });
259
355
 
260
356
  expect(contractCreated).toBe(false);
261
357
  expect(tokenId).toBe(0n);
262
358
 
359
+ const mintArguments: MintArguments = {
360
+ mintComment: "",
361
+ mintRecipient: collectorAccount,
362
+ mintReferral: collectorAccount,
363
+ };
364
+
263
365
  // now have the collector execute the first signed message;
264
366
  // it should create the contract, the token,
265
367
  // and min the quantity to mint tokens to the collector
@@ -267,16 +369,16 @@ describe("ZoraCreator1155Preminter", () => {
267
369
  // parameters are required to call this function
268
370
  const mintHash = await viemClients.walletClient.writeContract({
269
371
  abi: preminterAbi,
270
- functionName: "premint",
372
+ functionName: "premintV1",
271
373
  account: collectorAccount,
272
374
  chain: foundry,
273
375
  address: PREMINTER_ADDRESS,
274
376
  args: [
275
377
  contractConfig,
276
- premintConfig,
378
+ premintConfig1,
277
379
  signedMessage,
278
380
  quantityToMint,
279
- comment,
381
+ mintArguments,
280
382
  ],
281
383
  value: valueToSend,
282
384
  });
@@ -293,7 +395,7 @@ describe("ZoraCreator1155Preminter", () => {
293
395
  abi: preminterAbi,
294
396
  address: PREMINTER_ADDRESS,
295
397
  functionName: "premintStatus",
296
- args: [contractAddress, premintConfig.uid],
398
+ args: [contractAddress, premintConfig1.uid],
297
399
  });
298
400
 
299
401
  expect(contractCreated).toBe(true);
@@ -310,22 +412,19 @@ describe("ZoraCreator1155Preminter", () => {
310
412
  // get token balance - should be amount that was created
311
413
  expect(tokenBalance).toBe(quantityToMint);
312
414
 
313
- const premintConfig2 = {
314
- ...premintConfig,
315
- uid: premintConfig.uid + 1,
316
- tokenConfig: {
317
- ...premintConfig.tokenConfig,
318
- tokenURI: "ipfs://tokenIpfsId2",
319
- pricePerToken: parseEther("0.05"),
320
- },
321
- };
415
+ const premintConfig2 = defaultPremintConfigV2({
416
+ creatorAccount,
417
+ fixedPriceMinter: fixedPriceMinterAddress,
418
+ createReferral: creatorAccount,
419
+ });
322
420
 
323
421
  // sign the message to create the second token
324
422
  const signedMessage2 = await viemClients.walletClient.signTypedData({
325
- ...preminterTypedDataDefinition({
423
+ ...premintTypedDataDefinition({
326
424
  verifyingContract: contractAddress,
327
425
  chainId: foundry.id,
328
426
  premintConfig: premintConfig2,
427
+ premintConfigVersion: PremintConfigVersion.V2,
329
428
  }),
330
429
  account: creatorAccount,
331
430
  });
@@ -336,11 +435,9 @@ describe("ZoraCreator1155Preminter", () => {
336
435
  (ZORA_MINT_FEE + premintConfig2.tokenConfig.pricePerToken) *
337
436
  quantityToMint2;
338
437
 
339
- // now have the collector execute the second signed message.
340
- // it should create a new token against the existing contract
341
- const mintHash2 = await viemClients.walletClient.writeContract({
438
+ const simulationResult = await viemClients.publicClient.simulateContract({
342
439
  abi: preminterAbi,
343
- functionName: "premint",
440
+ functionName: "premintV2",
344
441
  account: collectorAccount,
345
442
  chain: foundry,
346
443
  address: PREMINTER_ADDRESS,
@@ -349,18 +446,23 @@ describe("ZoraCreator1155Preminter", () => {
349
446
  premintConfig2,
350
447
  signedMessage2,
351
448
  quantityToMint2,
352
- comment,
449
+ mintArguments,
353
450
  ],
354
451
  value: valueToSend2,
355
452
  });
356
453
 
357
- expect(
358
- (
359
- await viemClients.publicClient.waitForTransactionReceipt({
360
- hash: mintHash2,
361
- })
362
- ).status,
363
- ).toBe("success");
454
+ // now have the collector execute the second signed message.
455
+ // it should create a new token against the existing contract
456
+ const mintHash2 = await viemClients.walletClient.writeContract(
457
+ simulationResult.request,
458
+ );
459
+
460
+ const premintV2Receipt =
461
+ await viemClients.publicClient.waitForTransactionReceipt({
462
+ hash: mintHash2,
463
+ });
464
+
465
+ expect(premintV2Receipt.status).toBe("success");
364
466
 
365
467
  // now premint status for the second mint, it should be minted
366
468
  [, tokenId] = await viemClients.publicClient.readContract({
@@ -393,10 +495,10 @@ describe("ZoraCreator1155Preminter", () => {
393
495
  fixedPriceMinterAddress,
394
496
  accounts: { creatorAccount, collectorAccount },
395
497
  } = await setupContracts({ viemClients });
396
- const premintConfig = defaultPremintConfig(
397
- fixedPriceMinterAddress,
498
+ const premintConfig = defaultPremintConfigV2({
499
+ fixedPriceMinter: fixedPriceMinterAddress,
398
500
  creatorAccount,
399
- );
501
+ });
400
502
  const contractConfig = defaultContractConfig({
401
503
  contractAdmin: creatorAccount,
402
504
  });
@@ -411,14 +513,17 @@ describe("ZoraCreator1155Preminter", () => {
411
513
  args: [contractConfig],
412
514
  });
413
515
 
516
+ const signingChainId = foundry.id;
517
+
414
518
  // have creator sign the message to create the contract
415
519
  // and the token
416
520
  const signedMessage = await viemClients.walletClient.signTypedData({
417
- ...preminterTypedDataDefinition({
521
+ ...premintTypedDataDefinition({
418
522
  verifyingContract: contractAddress,
419
523
  // we need to sign here for the anvil chain, cause thats where it is run on
420
- chainId: foundry.id,
524
+ chainId: signingChainId,
421
525
  premintConfig,
526
+ premintConfigVersion: PremintConfigVersion.V2,
422
527
  }),
423
528
  account: creatorAccount,
424
529
  });
@@ -429,8 +534,6 @@ describe("ZoraCreator1155Preminter", () => {
429
534
  (ZORA_MINT_FEE + premintConfig.tokenConfig.pricePerToken) *
430
535
  quantityToMint;
431
536
 
432
- const comment = "I love this!";
433
-
434
537
  await viemClients.testClient.setBalance({
435
538
  address: collectorAccount,
436
539
  value: parseEther("10"),
@@ -443,7 +546,7 @@ describe("ZoraCreator1155Preminter", () => {
443
546
  // parameters are required to call this function
444
547
  const mintHash = await viemClients.walletClient.writeContract({
445
548
  abi: preminterAbi,
446
- functionName: "premint",
549
+ functionName: "premintV2",
447
550
  account: collectorAccount,
448
551
  chain: foundry,
449
552
  address: PREMINTER_ADDRESS,
@@ -452,7 +555,11 @@ describe("ZoraCreator1155Preminter", () => {
452
555
  premintConfig,
453
556
  signedMessage,
454
557
  quantityToMint,
455
- comment,
558
+ {
559
+ mintComment: "",
560
+ mintRecipient: collectorAccount,
561
+ mintReferral: zeroAddress,
562
+ },
456
563
  ],
457
564
  value: valueToSend,
458
565
  });
@@ -475,46 +582,16 @@ describe("ZoraCreator1155Preminter", () => {
475
582
 
476
583
  const creatorAttributionEvent = topics[0]!;
477
584
 
478
- const { creator, domainName, signature, structHash, version } =
479
- creatorAttributionEvent.args;
585
+ const { creator: creatorFromEvent } = creatorAttributionEvent.args;
480
586
 
481
- const chainId = foundry.id;
587
+ const recoveredSigner = await recoverCreatorFromCreatorAttribution({
588
+ creatorAttribution: creatorAttributionEvent.args,
589
+ chainId: signingChainId,
590
+ tokenContract: contractAddress,
591
+ });
482
592
 
483
- // hash the eip712 domain based on the parameters emitted from the event:
484
- const hashedDomain = hashDomain({
485
- domain: {
486
- chainId,
487
- name: domainName,
488
- verifyingContract: contractAddress,
489
- version,
490
- },
491
- types: {
492
- EIP712Domain: [
493
- { name: "name", type: "string" },
494
- { name: "version", type: "string" },
495
- {
496
- name: "chainId",
497
- type: "uint256",
498
- },
499
- {
500
- name: "verifyingContract",
501
- type: "address",
502
- },
503
- ],
504
- },
505
- });
506
-
507
- // re-build the eip-712 typed data hash, consisting of the hashed domain and the structHash emitted from the event:
508
- const parts: Hex[] = ["0x1901", hashedDomain, structHash!];
509
-
510
- const hashedTypedData = keccak256(concat(parts));
511
-
512
- const recoveredSigner = await recoverAddress({
513
- hash: hashedTypedData,
514
- signature: signature!,
515
- });
516
-
517
- expect(recoveredSigner).toBe(creator);
593
+ expect(creatorFromEvent).toBe(creatorAccount);
594
+ expect(recoveredSigner).toBe(creatorFromEvent);
518
595
  },
519
596
  );
520
597
  });