@zoralabs/protocol-sdk 0.5.8-DEV.2 → 0.5.9

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,19 +1,29 @@
1
1
  import {
2
2
  zoraMints1155Config,
3
+ zoraMintsManagerImplABI,
3
4
  zoraMintsManagerImplConfig,
5
+ mintsEthUnwrapperAndCallerConfig,
6
+ iUnwrapAndForwardActionABI,
4
7
  } from "@zoralabs/protocol-deployments";
5
8
  import { AbiParametersToPrimitiveTypes, ExtractAbiFunction } from "abitype";
6
- import { MintArguments, PremintConfigV2 } from "src/premint/contract-types";
9
+ import {
10
+ MintArguments as PremintMintArguments,
11
+ PremintConfigV2,
12
+ } from "src/premint/contract-types";
7
13
  import { ContractCreationConfig } from "src/preminter";
8
14
  import {
9
15
  Account,
10
16
  Address,
17
+ ContractFunctionRevertedError,
11
18
  Hex,
12
19
  ReadContractParameters,
13
20
  SignTypedDataParameters,
14
21
  SimulateContractParameters,
15
22
  TypedData,
23
+ decodeErrorResult,
24
+ encodeAbiParameters,
16
25
  encodeFunctionData,
26
+ parseAbiParameters,
17
27
  zeroAddress,
18
28
  } from "viem";
19
29
 
@@ -49,6 +59,12 @@ const getPaidMintValue = (quantities: bigint[], pricePerMint?: bigint) => {
49
59
  return quantities.reduce((a, b) => a + b, 0n) * pricePerMint;
50
60
  };
51
61
 
62
+ export const fixedPriceMinterMinterArguments = ({
63
+ mintRecipient,
64
+ }: {
65
+ mintRecipient: Address;
66
+ }) => encodeAbiParameters(parseAbiParameters("address"), [mintRecipient]);
67
+
52
68
  export const mintsBalanceOfAccountParams = ({
53
69
  account,
54
70
  chainId,
@@ -65,31 +81,16 @@ export const mintsBalanceOfAccountParams = ({
65
81
  args: [account],
66
82
  });
67
83
 
68
- export const accountNonceParams = ({
69
- chainId,
70
- account,
71
- }: {
72
- chainId: keyof typeof zoraMints1155Config.address;
73
- account: Address;
74
- }): ReadContractParameters<typeof zoraMints1155Config.abi, "nonces"> => ({
75
- abi: zoraMints1155Config.abi,
76
- address: zoraMints1155Config.address[chainId],
77
- functionName: "nonces",
78
- args: [account],
79
- });
80
-
81
84
  type CollectOnManagerParams = {
82
85
  tokenIds: bigint[];
83
86
  quantities: bigint[];
84
87
  zoraCreator1155Contract: Address;
85
88
  zoraCreator1155TokenId: bigint;
86
89
  minter: Address;
87
- mintArguments: MintArguments;
90
+ mintArguments: CollectMintArguments;
88
91
  };
89
92
 
90
93
  export const encodeCollectOnManager = ({
91
- tokenIds,
92
- quantities,
93
94
  zoraCreator1155Contract,
94
95
  minter,
95
96
  zoraCreator1155TokenId,
@@ -99,8 +100,6 @@ export const encodeCollectOnManager = ({
99
100
  abi: zoraMintsManagerImplConfig.abi,
100
101
  functionName: "collect",
101
102
  args: [
102
- tokenIds,
103
- quantities,
104
103
  zoraCreator1155Contract,
105
104
  minter,
106
105
  zoraCreator1155TokenId,
@@ -134,7 +133,7 @@ export function collectWithMintsParams({
134
133
  abi: zoraMints1155Config.abi,
135
134
  address: zoraMints1155Config.address[chainId],
136
135
  functionName: "transferBatchToManagerAndCall",
137
- args: [tokenIds, quantities, "0x", call],
136
+ args: [tokenIds, quantities, call],
138
137
  value: getPaidMintValue(quantities, pricePerToken),
139
138
  account,
140
139
  };
@@ -154,20 +153,23 @@ export const collectWithMintsTypedDataDefinition = ({
154
153
  deadline: bigint;
155
154
  account: Account | Address;
156
155
  } & CollectOnManagerParams) => {
157
- const call = encodeCollectOnManager({
156
+ const safeTransferData = encodeCollectOnManager({
158
157
  tokenIds,
159
158
  quantities,
160
159
  ...rest,
161
160
  });
162
161
 
163
- return makePermitAndTypeData({
162
+ return makePermitTransferBatchAndTypeData({
164
163
  tokenIds,
165
164
  quantities,
166
165
  chainId,
167
166
  account,
168
167
  nonce,
169
168
  deadline,
170
- call,
169
+ safeTransferData,
170
+ // will safe transfer to manager contract before doing the
171
+ // collect operation
172
+ to: zoraMintsManagerImplConfig.address[chainId],
171
173
  });
172
174
  };
173
175
 
@@ -185,31 +187,33 @@ export const collectPremintWithMintsTypedDataDefinition = ({
185
187
  deadline: bigint;
186
188
  account: Account | Address;
187
189
  } & PremintOnManagerParams) => {
188
- const call = encodePremintOnManager({
189
- tokenIds,
190
- quantities,
190
+ const safeTransferData = encodePremintOnManager({
191
191
  ...rest,
192
192
  });
193
193
 
194
- return makePermitAndTypeData({
194
+ return makePermitTransferBatchAndTypeData({
195
195
  tokenIds,
196
196
  quantities,
197
197
  chainId,
198
198
  account,
199
199
  nonce,
200
200
  deadline,
201
- call,
201
+ safeTransferData,
202
+ // will safe transfer to manager contract before doing the
203
+ // collect operation
204
+ to: zoraMintsManagerImplConfig.address[chainId],
202
205
  });
203
206
  };
204
207
 
205
- function makePermitAndTypeData({
208
+ function makePermitTransferBatchAndTypeData({
206
209
  tokenIds,
207
210
  quantities,
208
211
  chainId,
209
212
  account,
213
+ to,
210
214
  nonce,
211
215
  deadline,
212
- call,
216
+ safeTransferData,
213
217
  }: {
214
218
  tokenIds: bigint[];
215
219
  quantities: bigint[];
@@ -217,20 +221,62 @@ function makePermitAndTypeData({
217
221
  nonce: bigint;
218
222
  deadline: bigint;
219
223
  account: Account | Address;
220
- call: Hex;
224
+ to: Address;
225
+ safeTransferData: Hex;
221
226
  }) {
222
- const permit: PermitTransferBatchToManager = {
227
+ const permit: PermitSafeTransferBatch = {
223
228
  owner: typeof account === "string" ? account : account.address,
229
+ to,
224
230
  tokenIds,
225
231
  quantities,
226
- call,
227
232
  deadline,
228
- safeTransferData: "0x",
233
+ nonce,
234
+ safeTransferData,
229
235
  };
230
236
 
231
- const typedData = permitTypedDataDefinition({
237
+ const typedData = permitBatchTypedDataDefinition({
232
238
  chainId,
239
+ permit,
240
+ account,
241
+ });
242
+
243
+ return {
244
+ permit,
245
+ typedData,
246
+ };
247
+ }
248
+
249
+ function makePermitTransferTypeData({
250
+ tokenId,
251
+ quantity,
252
+ chainId,
253
+ account,
254
+ to,
255
+ nonce,
256
+ deadline,
257
+ safeTransferData,
258
+ }: {
259
+ tokenId: bigint;
260
+ quantity: bigint;
261
+ chainId: keyof typeof zoraMints1155Config.address;
262
+ nonce: bigint;
263
+ deadline: bigint;
264
+ account: Account | Address;
265
+ to: Address;
266
+ safeTransferData: Hex;
267
+ }) {
268
+ const permit: PermitSafeTransfer = {
269
+ owner: typeof account === "string" ? account : account.address,
270
+ to,
271
+ tokenId,
272
+ quantity,
273
+ deadline,
233
274
  nonce,
275
+ safeTransferData,
276
+ };
277
+
278
+ const typedData = permitTransferTypedDataDefinition({
279
+ chainId,
234
280
  permit,
235
281
  account,
236
282
  });
@@ -247,25 +293,21 @@ type PremintOnManagerParams = {
247
293
  contractCreationConfig: ContractCreationConfig;
248
294
  premintConfig: PremintConfigV2;
249
295
  premintSignature: Hex;
250
- mintArguments: MintArguments;
296
+ mintArguments: PremintMintArguments;
251
297
  signerContract?: Address;
252
298
  };
253
299
 
254
300
  export const encodePremintOnManager = ({
255
- tokenIds,
256
- quantities,
257
301
  contractCreationConfig,
258
302
  premintConfig,
259
303
  premintSignature,
260
304
  mintArguments,
261
305
  signerContract = zeroAddress,
262
- }: PremintOnManagerParams) =>
306
+ }: Omit<PremintOnManagerParams, "tokenIds" | "quantities">) =>
263
307
  encodeFunctionData({
264
308
  abi: zoraMintsManagerImplConfig.abi,
265
309
  functionName: "collectPremintV2",
266
310
  args: [
267
- tokenIds,
268
- quantities,
269
311
  contractCreationConfig,
270
312
  premintConfig,
271
313
  premintSignature,
@@ -290,8 +332,6 @@ export function collectPremintV2WithMintsParams({
290
332
  "transferBatchToManagerAndCall"
291
333
  > {
292
334
  const call = encodePremintOnManager({
293
- tokenIds,
294
- quantities,
295
335
  ...rest,
296
336
  });
297
337
 
@@ -299,18 +339,29 @@ export function collectPremintV2WithMintsParams({
299
339
  abi: zoraMints1155Config.abi,
300
340
  address: zoraMints1155Config.address[chainId],
301
341
  functionName: "transferBatchToManagerAndCall",
302
- args: [tokenIds, quantities, "0x", call],
342
+ args: [tokenIds, quantities, call],
303
343
  value: getPaidMintValue(quantities, pricePerToken),
304
344
  account,
305
345
  };
306
346
  }
307
347
 
308
- export type PermitTransferBatchToManager = AbiParametersToPrimitiveTypes<
348
+ export type PermitSafeTransferBatch = AbiParametersToPrimitiveTypes<
309
349
  ExtractAbiFunction<
310
350
  typeof zoraMints1155Config.abi,
311
- "permitTransferBatchToManagerAndCall"
351
+ "permitSafeTransferBatch"
312
352
  >["inputs"]
313
353
  >[0];
354
+
355
+ export type PermitSafeTransfer = AbiParametersToPrimitiveTypes<
356
+ ExtractAbiFunction<
357
+ typeof zoraMints1155Config.abi,
358
+ "permitSafeTransfer"
359
+ >["inputs"]
360
+ >[0];
361
+
362
+ export type CollectMintArguments = AbiParametersToPrimitiveTypes<
363
+ ExtractAbiFunction<typeof zoraMintsManagerImplConfig.abi, "collect">["inputs"]
364
+ >[3];
314
365
  // export type PermitTransferBatchToManager = ExtractAbiFunction<
315
366
 
316
367
  function makeTypeData<
@@ -320,15 +371,13 @@ function makeTypeData<
320
371
  return args;
321
372
  }
322
373
 
323
- export function permitTypedDataDefinition({
374
+ export function permitBatchTypedDataDefinition({
324
375
  permit,
325
376
  chainId,
326
- nonce,
327
377
  account,
328
378
  }: {
329
- permit: PermitTransferBatchToManager;
379
+ permit: PermitSafeTransferBatch;
330
380
  chainId: keyof typeof zoraMints1155Config.address;
331
- nonce: bigint;
332
381
  account: Account | Address;
333
382
  }) {
334
383
  return makeTypeData({
@@ -339,6 +388,10 @@ export function permitTypedDataDefinition({
339
388
  name: "owner",
340
389
  type: "address",
341
390
  },
391
+ {
392
+ name: "to",
393
+ type: "address",
394
+ },
342
395
  {
343
396
  name: "tokenIds",
344
397
  type: "uint256[]",
@@ -352,7 +405,58 @@ export function permitTypedDataDefinition({
352
405
  type: "bytes",
353
406
  },
354
407
  {
355
- name: "call",
408
+ name: "nonce",
409
+ type: "uint256",
410
+ },
411
+ {
412
+ name: "deadline",
413
+ type: "uint256",
414
+ },
415
+ ],
416
+ },
417
+ message: permit,
418
+ domain: {
419
+ chainId,
420
+ name: "Mints",
421
+ version: "1",
422
+ verifyingContract: zoraMints1155Config.address[chainId],
423
+ },
424
+ // signing account must be permit owner
425
+ account,
426
+ });
427
+ }
428
+
429
+ export function permitTransferTypedDataDefinition({
430
+ permit,
431
+ chainId,
432
+ account,
433
+ }: {
434
+ permit: PermitSafeTransfer;
435
+ chainId: keyof typeof zoraMints1155Config.address;
436
+ account: Account | Address;
437
+ }) {
438
+ return makeTypeData({
439
+ primaryType: "PermitSafeTransfer",
440
+ types: {
441
+ PermitSafeTransfer: [
442
+ {
443
+ name: "owner",
444
+ type: "address",
445
+ },
446
+ {
447
+ name: "to",
448
+ type: "address",
449
+ },
450
+ {
451
+ name: "tokenId",
452
+ type: "uint256",
453
+ },
454
+ {
455
+ name: "quantity",
456
+ type: "uint256",
457
+ },
458
+ {
459
+ name: "safeTransferData",
356
460
  type: "bytes",
357
461
  },
358
462
  {
@@ -365,15 +469,7 @@ export function permitTypedDataDefinition({
365
469
  },
366
470
  ],
367
471
  },
368
- message: {
369
- owner: permit.owner,
370
- tokenIds: permit.tokenIds,
371
- quantities: permit.quantities,
372
- safeTransferData: permit.safeTransferData,
373
- call: permit.call,
374
- nonce,
375
- deadline: permit.deadline,
376
- },
472
+ message: permit,
377
473
  domain: {
378
474
  chainId,
379
475
  name: "Mints",
@@ -390,19 +486,170 @@ export const permitTransferBatchToManagerAndCallParams = ({
390
486
  chainId,
391
487
  signature,
392
488
  }: {
393
- permit: PermitTransferBatchToManager;
489
+ permit: PermitSafeTransferBatch;
394
490
  chainId: keyof typeof zoraMints1155Config.address;
395
491
  signature: Hex;
396
492
  }) => {
397
493
  const result: SimulateContractParameters<
398
494
  typeof zoraMints1155Config.abi,
399
- "permitTransferBatchToManagerAndCall"
495
+ "permitSafeTransferBatch"
400
496
  > = {
401
497
  abi: zoraMints1155Config.abi,
402
498
  address: zoraMints1155Config.address[chainId],
403
- functionName: "permitTransferBatchToManagerAndCall",
499
+ functionName: "permitSafeTransferBatch",
404
500
  args: [permit, signature],
405
501
  };
406
502
 
407
503
  return result;
408
504
  };
505
+
506
+ export const safeTransferAndUnwrapTypedDataDefinition = ({
507
+ chainId,
508
+ tokenId,
509
+ quantity,
510
+ from,
511
+ addressToCall,
512
+ functionToCall,
513
+ valueToSend,
514
+ deadline,
515
+ nonce,
516
+ }: {
517
+ tokenId: bigint;
518
+ quantity: bigint;
519
+ chainId: keyof typeof zoraMints1155Config.address;
520
+ // mints will be transferred from this address,
521
+ // must match the callers address
522
+ from: Address | Account;
523
+ addressToCall: Address;
524
+ functionToCall: Hex;
525
+ valueToSend: bigint;
526
+ deadline: bigint;
527
+ nonce: bigint;
528
+ }) => {
529
+ // this is the call that the wrapper will call
530
+ const callArgument = encodeFunctionData({
531
+ abi: iUnwrapAndForwardActionABI,
532
+ functionName: "callWithEth",
533
+ args: [addressToCall, functionToCall, valueToSend],
534
+ });
535
+
536
+ return makePermitTransferTypeData({
537
+ account: from,
538
+ chainId,
539
+ deadline,
540
+ tokenId,
541
+ quantity,
542
+ safeTransferData: callArgument,
543
+ to: mintsEthUnwrapperAndCallerConfig.address[chainId],
544
+ nonce,
545
+ });
546
+ };
547
+
548
+ export const safeTransferBatchAndUnwrapTypedDataDefinition = ({
549
+ chainId,
550
+ tokenIds,
551
+ quantities,
552
+ from,
553
+ addressToCall,
554
+ functionToCall,
555
+ valueToSend,
556
+ deadline,
557
+ nonce,
558
+ }: {
559
+ tokenIds: bigint[];
560
+ quantities: bigint[];
561
+ chainId: keyof typeof zoraMints1155Config.address;
562
+ // mints will be transferred from this address,
563
+ // must match the callers address
564
+ from: Address | Account;
565
+ addressToCall: Address;
566
+ functionToCall: Hex;
567
+ valueToSend: bigint;
568
+ deadline: bigint;
569
+ nonce: bigint;
570
+ }) => {
571
+ // this is the call that the wrapper will call
572
+ const callArgument = encodeFunctionData({
573
+ abi: iUnwrapAndForwardActionABI,
574
+ functionName: "callWithEth",
575
+ args: [addressToCall, functionToCall, valueToSend],
576
+ });
577
+
578
+ return makePermitTransferBatchAndTypeData({
579
+ account: from,
580
+ chainId,
581
+ deadline,
582
+ tokenIds,
583
+ quantities,
584
+ safeTransferData: callArgument,
585
+ to: mintsEthUnwrapperAndCallerConfig.address[chainId],
586
+ nonce,
587
+ });
588
+ };
589
+
590
+ export const safeTransferAndUnwrapEthParams = ({
591
+ chainId,
592
+ tokenIds,
593
+ quantities,
594
+ from,
595
+ addressToCall,
596
+ functionToCall,
597
+ valueToSend,
598
+ }: {
599
+ tokenIds: bigint[];
600
+ quantities: bigint[];
601
+ chainId: keyof typeof zoraMints1155Config.address;
602
+ // mints will be transferred from this address,
603
+ // must match the callers address
604
+ from: Address | Account;
605
+ addressToCall: Address;
606
+ functionToCall: Hex;
607
+ valueToSend: bigint;
608
+ }) => {
609
+ // this is the encoded calldata for the eth unwrapper to
610
+ // read and execute.
611
+ // for valueToSend, whatever is remaining will be refunded
612
+ // to the recipient.
613
+ const callArgument = encodeFunctionData({
614
+ abi: iUnwrapAndForwardActionABI,
615
+ functionName: "callWithEth",
616
+ args: [addressToCall, functionToCall, valueToSend],
617
+ });
618
+
619
+ const result: SimulateContractParameters<
620
+ typeof zoraMints1155Config.abi,
621
+ "safeBatchTransferFrom"
622
+ > = {
623
+ abi: zoraMints1155Config.abi,
624
+ functionName: "safeBatchTransferFrom",
625
+ address: zoraMints1155Config.address[chainId],
626
+ args: [
627
+ typeof from === "string" ? from : from.address,
628
+ // the mints will be transferred to this address, which
629
+ // will burn/redeem their eth value
630
+ mintsEthUnwrapperAndCallerConfig.address[chainId],
631
+ // token ids to transfer/burn/unwrap - must be eth based tokens
632
+ tokenIds,
633
+ // quantities to transfer/burn/unwrap
634
+ quantities,
635
+ // this is the safeTransferData - which gets forwarded to the eth transferrer
636
+ callArgument,
637
+ ],
638
+ // the account whos mints will be transferred from
639
+ account: from,
640
+ };
641
+
642
+ return result;
643
+ };
644
+
645
+ export function decodeCallFailedError(error: ContractFunctionRevertedError) {
646
+ if (error.data?.errorName !== "CallFailed")
647
+ throw new Error("Not a CallFailed error");
648
+
649
+ const internalErrorData = error.data?.args?.[0] as Hex;
650
+
651
+ return decodeErrorResult({
652
+ abi: zoraMintsManagerImplABI,
653
+ data: internalErrorData,
654
+ });
655
+ }
@@ -0,0 +1,105 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ MintAccountBalance,
4
+ selectMintsToCollectWithFromQueryResult,
5
+ sumBalances,
6
+ } from "./mints-queries";
7
+
8
+ describe("MINTs queries", () => {
9
+ describe("selectMintsToCollectWithFromQueryResult", () => {
10
+ it("should return the optimum tokenIds and quantities to collect", async () => {
11
+ // account has 3 of token 1, 4 of token 2, 10 of token 3
12
+ // token 1 price is 2, token 2 price is 1, token 3 price is 3
13
+ // we want to collect 10 tokens
14
+ // we should return token 2 with 4 tokens, token 1 with 3 tokens, and token 3 with 3 tokens
15
+ const mintTokenBalances: MintAccountBalance[] = [
16
+ {
17
+ mintToken: {
18
+ id: "1",
19
+ pricePerToken: "2",
20
+ },
21
+ balance: "3",
22
+ },
23
+ {
24
+ mintToken: {
25
+ id: "2",
26
+ pricePerToken: "1",
27
+ },
28
+ balance: "4",
29
+ },
30
+ {
31
+ mintToken: {
32
+ id: "3",
33
+ pricePerToken: "3",
34
+ },
35
+ balance: "10",
36
+ },
37
+ ];
38
+
39
+ const result = selectMintsToCollectWithFromQueryResult(
40
+ mintTokenBalances,
41
+ 10n,
42
+ );
43
+
44
+ const expectedResult = {
45
+ tokenIds: [2n, 1n, 3n],
46
+ quantities: [4n, 3n, 3n],
47
+ };
48
+
49
+ expect(result).toEqual(expectedResult);
50
+ });
51
+
52
+ it("should throw an error if not enough tokens to collect with", () => {
53
+ const mintTokenBalances: MintAccountBalance[] = [
54
+ {
55
+ mintToken: {
56
+ id: "1",
57
+ pricePerToken: "2",
58
+ },
59
+ balance: "3",
60
+ },
61
+ {
62
+ mintToken: {
63
+ id: "2",
64
+ pricePerToken: "1",
65
+ },
66
+ balance: "4",
67
+ },
68
+ ];
69
+ const quantityToCollect = 8n;
70
+
71
+ expect(() => {
72
+ selectMintsToCollectWithFromQueryResult(
73
+ mintTokenBalances,
74
+ quantityToCollect,
75
+ );
76
+ }).toThrowError("Not enough MINTs to collect with");
77
+ });
78
+ });
79
+
80
+ describe("sumBalances", () => {
81
+ it("should return the sum of the balances", async () => {
82
+ // account has 3 of token 1, 4 of token 2, 10 of token 3
83
+ // token 1 price is 2, token 2 price is 1, token 3 price is 3
84
+ // we want to collect 10 tokens
85
+ // we should return token 2 with 4 tokens, token 1 with 3 tokens, and token 3 with 3 tokens
86
+ const mintTokenBalances: Pick<MintAccountBalance, "balance">[] = [
87
+ {
88
+ balance: "3",
89
+ },
90
+ {
91
+ balance: "4",
92
+ },
93
+ {
94
+ balance: "10",
95
+ },
96
+ ];
97
+
98
+ const result = sumBalances(mintTokenBalances);
99
+
100
+ const expectedResult = 3n + 4n + 10n;
101
+
102
+ expect(result).toEqual(expectedResult);
103
+ });
104
+ });
105
+ });