@silvana-one/nft 0.3.0 → 1.0.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.
Files changed (87) hide show
  1. package/README.md +12 -0
  2. package/dist/node/admin/advanced.d.ts +5 -1
  3. package/dist/node/contracts/admin.d.ts +100 -10
  4. package/dist/node/contracts/admin.js +130 -29
  5. package/dist/node/contracts/admin.js.map +1 -1
  6. package/dist/node/contracts/collection.d.ts +56 -35
  7. package/dist/node/contracts/collection.js +162 -71
  8. package/dist/node/contracts/collection.js.map +1 -1
  9. package/dist/node/contracts/nft.js +3 -0
  10. package/dist/node/contracts/nft.js.map +1 -1
  11. package/dist/node/contracts.d.ts +51 -33
  12. package/dist/node/index.cjs +447 -182
  13. package/dist/node/interfaces/collection.d.ts +5 -5
  14. package/dist/node/interfaces/events.d.ts +306 -16
  15. package/dist/node/interfaces/events.js +42 -4
  16. package/dist/node/interfaces/events.js.map +1 -1
  17. package/dist/node/interfaces/ownable.d.ts +7 -7
  18. package/dist/node/interfaces/ownable.js.map +1 -1
  19. package/dist/node/interfaces/pausable.d.ts +3 -3
  20. package/dist/node/interfaces/pausable.js.map +1 -1
  21. package/dist/node/interfaces/types.d.ts +182 -27
  22. package/dist/node/interfaces/types.js +120 -71
  23. package/dist/node/interfaces/types.js.map +1 -1
  24. package/dist/node/marketplace/auction.d.ts +7 -3
  25. package/dist/node/marketplace/auction.js +4 -4
  26. package/dist/node/marketplace/auction.js.map +1 -1
  27. package/dist/node/marketplace/bid.d.ts +5 -1
  28. package/dist/node/marketplace/bid.js +3 -5
  29. package/dist/node/marketplace/bid.js.map +1 -1
  30. package/dist/node/marketplace/nft-shares.d.ts +17 -5
  31. package/dist/node/marketplace/offer.d.ts +5 -1
  32. package/dist/node/util/div.js +10 -5
  33. package/dist/node/util/div.js.map +1 -1
  34. package/dist/node/vk.js +6 -6
  35. package/dist/node/vk.js.map +1 -1
  36. package/dist/node/zkprogram-example/game.d.ts +6 -17
  37. package/dist/node/zkprogram-example/update.d.ts +6 -17
  38. package/dist/tsconfig.node.tsbuildinfo +1 -1
  39. package/dist/tsconfig.web.tsbuildinfo +1 -1
  40. package/dist/web/admin/advanced.d.ts +5 -1
  41. package/dist/web/contracts/admin.d.ts +100 -10
  42. package/dist/web/contracts/admin.js +130 -29
  43. package/dist/web/contracts/admin.js.map +1 -1
  44. package/dist/web/contracts/collection.d.ts +56 -35
  45. package/dist/web/contracts/collection.js +162 -71
  46. package/dist/web/contracts/collection.js.map +1 -1
  47. package/dist/web/contracts/nft.js +3 -0
  48. package/dist/web/contracts/nft.js.map +1 -1
  49. package/dist/web/contracts.d.ts +51 -33
  50. package/dist/web/interfaces/collection.d.ts +5 -5
  51. package/dist/web/interfaces/events.d.ts +306 -16
  52. package/dist/web/interfaces/events.js +42 -4
  53. package/dist/web/interfaces/events.js.map +1 -1
  54. package/dist/web/interfaces/ownable.d.ts +7 -7
  55. package/dist/web/interfaces/ownable.js.map +1 -1
  56. package/dist/web/interfaces/pausable.d.ts +3 -3
  57. package/dist/web/interfaces/pausable.js.map +1 -1
  58. package/dist/web/interfaces/types.d.ts +182 -27
  59. package/dist/web/interfaces/types.js +120 -71
  60. package/dist/web/interfaces/types.js.map +1 -1
  61. package/dist/web/marketplace/auction.d.ts +7 -3
  62. package/dist/web/marketplace/auction.js +4 -4
  63. package/dist/web/marketplace/auction.js.map +1 -1
  64. package/dist/web/marketplace/bid.d.ts +5 -1
  65. package/dist/web/marketplace/bid.js +3 -5
  66. package/dist/web/marketplace/bid.js.map +1 -1
  67. package/dist/web/marketplace/nft-shares.d.ts +17 -5
  68. package/dist/web/marketplace/offer.d.ts +5 -1
  69. package/dist/web/util/div.js +10 -5
  70. package/dist/web/util/div.js.map +1 -1
  71. package/dist/web/vk.js +6 -6
  72. package/dist/web/vk.js.map +1 -1
  73. package/dist/web/zkprogram-example/game.d.ts +6 -17
  74. package/dist/web/zkprogram-example/update.d.ts +6 -17
  75. package/package.json +13 -13
  76. package/src/contracts/admin.ts +137 -24
  77. package/src/contracts/collection.ts +188 -96
  78. package/src/contracts/nft.ts +3 -0
  79. package/src/interfaces/collection.ts +11 -5
  80. package/src/interfaces/events.ts +47 -3
  81. package/src/interfaces/ownable.ts +1 -1
  82. package/src/interfaces/pausable.ts +1 -1
  83. package/src/interfaces/types.ts +152 -78
  84. package/src/marketplace/auction.ts +5 -4
  85. package/src/marketplace/bid.ts +4 -6
  86. package/src/util/div.ts +12 -5
  87. package/src/vk.ts +6 -6
@@ -8,7 +8,7 @@
8
8
  import { __decorate, __metadata } from "tslib";
9
9
  import { Field, PublicKey, AccountUpdate, Bool, method, state, State, Permissions, TokenContract, VerificationKey, UInt32, UInt64, Mina, Provable, } from "o1js";
10
10
  import { NFT } from "./nft.js";
11
- import { MintParams, MintRequest, TransferParams, CollectionData, NFTUpdateProof, NFTStateStruct, MintEvent, TransferEvent, ApproveEvent, UpgradeVerificationKeyEvent, LimitMintingEvent, PauseNFTEvent, PauseEvent, OwnershipChangeEvent, UInt64Option, MAX_ROYALTY_FEE, TransferExtendedParams, } from "../interfaces/index.js";
11
+ import { MintParams, MintRequest, TransferBySignatureParams, TransferByProofParams, CollectionData, NFTUpdateProof, NFTStateStruct, MintEvent, NFTUpdateEvent, TransferEvent, ApproveEvent, UpgradeVerificationKeyEvent, LimitMintingEvent, PauseNFTEvent, PauseEvent, SetNameEvent, SetBaseURLEvent, SetRoyaltyFeeEvent, SetTransferFeeEvent, SetAdminEvent, OwnershipChangeEvent, UInt64Option, MAX_ROYALTY_FEE, TransferExtendedParams, } from "../interfaces/index.js";
12
12
  import { mulDiv } from "../util/index.js";
13
13
  import { nftVerificationKeys } from "../vk.js";
14
14
  export { CollectionFactory, CollectionErrors };
@@ -34,6 +34,7 @@ const CollectionErrors = {
34
34
  onlyOwnerCanUpgradeVerificationKey: "Only owner can upgrade verification key",
35
35
  invalidRoyaltyFee: "Royalty fee is too high, cannot be more than 100%",
36
36
  invalidOracleAddress: "Oracle address is invalid",
37
+ pendingCreatorIsEmpty: "Pending creator address is empty",
37
38
  };
38
39
  /**
39
40
  * Creates a new NFT Collection Contract class.
@@ -63,27 +64,30 @@ function CollectionFactory(params) {
63
64
  * such as flags and fee configurations.
64
65
  */
65
66
  this.packedData = State();
67
+ /** The public key part (x) of the pending creator. The isOdd field is written to the packedData */
68
+ this.pendingCreatorX = State();
66
69
  /**
67
70
  * Defines the events emitted by the contract.
68
71
  */
69
72
  this.events = {
70
73
  mint: MintEvent,
71
- update: PublicKey,
74
+ update: NFTUpdateEvent,
72
75
  transfer: TransferEvent,
73
76
  approve: ApproveEvent,
74
77
  upgradeNFTVerificationKey: UpgradeVerificationKeyEvent,
75
- upgradeVerificationKey: Field,
78
+ upgradeVerificationKey: UpgradeVerificationKeyEvent,
76
79
  limitMinting: LimitMintingEvent,
77
80
  pause: PauseEvent,
78
81
  resume: PauseEvent,
79
82
  pauseNFT: PauseNFTEvent,
80
83
  resumeNFT: PauseNFTEvent,
81
- ownershipChange: OwnershipChangeEvent,
82
- setName: Field,
83
- setBaseURL: Field,
84
- setRoyaltyFee: UInt32,
85
- setTransferFee: UInt64,
86
- setAdmin: PublicKey,
84
+ ownershipTransfer: OwnershipChangeEvent,
85
+ ownershipAccepted: OwnershipChangeEvent,
86
+ setName: SetNameEvent,
87
+ setBaseURL: SetBaseURLEvent,
88
+ setRoyaltyFee: SetRoyaltyFeeEvent,
89
+ setTransferFee: SetTransferFeeEvent,
90
+ setAdmin: SetAdminEvent,
87
91
  };
88
92
  }
89
93
  /**
@@ -101,6 +105,9 @@ function CollectionFactory(params) {
101
105
  this.packedData.set(CollectionData.new({
102
106
  isPaused: true,
103
107
  }).pack());
108
+ this.pendingCreatorX.set(PublicKey.empty().x);
109
+ // Changes must be made if the number of state fields available on the Mina blockchain changes
110
+ // This function should initialize ALL state fields due to the logic in the initialize() method
104
111
  this.account.zkappUri.set(props.url);
105
112
  this.account.tokenSymbol.set(props.symbol);
106
113
  this.account.permissions.set({
@@ -109,8 +116,8 @@ function CollectionFactory(params) {
109
116
  setPermissions: Permissions.impossible(),
110
117
  access: Permissions.proof(),
111
118
  send: Permissions.proof(),
112
- setZkappUri: Permissions.none(),
113
- setTokenSymbol: Permissions.none(),
119
+ setZkappUri: Permissions.proof(),
120
+ setTokenSymbol: Permissions.proof(),
114
121
  });
115
122
  }
116
123
  /**
@@ -120,6 +127,9 @@ function CollectionFactory(params) {
120
127
  * @param collectionData - Initial collection data including flags and configurations.
121
128
  */
122
129
  async initialize(masterNFT, collectionData) {
130
+ // Changes must be made if the number of state fields available on the Mina blockchain changes
131
+ // as the next line relies on the fact that the state size is 8 Fields
132
+ // and all 8 Field are initialized in deploy()
123
133
  this.account.provedState.requireEquals(Bool(false));
124
134
  collectionData.royaltyFee.assertLessThanOrEqual(UInt32.from(MAX_ROYALTY_FEE), CollectionErrors.invalidRoyaltyFee);
125
135
  this.packedData.set(collectionData.pack());
@@ -200,7 +210,9 @@ function CollectionFactory(params) {
200
210
  * @returns The packed data of the collection.
201
211
  */
202
212
  async ensureNotPaused() {
203
- CollectionData.isPaused(this.packedData.getAndRequireEquals()).assertFalse(CollectionErrors.collectionPaused);
213
+ const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
214
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
215
+ return collectionData;
204
216
  }
205
217
  /**
206
218
  * Mints a new NFT directly by the creator.
@@ -217,7 +229,8 @@ function CollectionFactory(params) {
217
229
  * @param params - The mint parameters containing details of the NFT to be minted.
218
230
  */
219
231
  async mintByCreator(params) {
220
- CollectionData.mintingIsLimited(this.packedData.getAndRequireEquals()).assertFalse(CollectionErrors.cannotMint);
232
+ const collectionData = await this.ensureNotPaused();
233
+ collectionData.mintingIsLimited.assertFalse(CollectionErrors.cannotMint);
221
234
  const creatorUpdate = await this.ensureCreatorSignature();
222
235
  // Pay 1 MINA fee for a new account
223
236
  creatorUpdate.balance.subInPlace(1_000_000_000);
@@ -229,10 +242,13 @@ function CollectionFactory(params) {
229
242
  * @param mintRequest - The minting request containing parameters and proofs.
230
243
  */
231
244
  async mint(mintRequest) {
232
- CollectionData.mintingIsLimited(this.packedData.getAndRequireEquals()).assertFalse(CollectionErrors.cannotMint);
245
+ const collectionData = await this.ensureNotPaused();
246
+ collectionData.mintingIsLimited.assertFalse(CollectionErrors.cannotMint);
233
247
  const adminContract = this.getAdminContract();
234
248
  // The admin contract checks that the sender is allowed to mint
235
249
  const mintParams = (await adminContract.canMint(mintRequest)).assertSome(CollectionErrors.cannotMint);
250
+ mintParams.address.assertEquals(mintRequest.address);
251
+ mintParams.data.owner.assertEquals(mintRequest.owner);
236
252
  // Prevent minting the Master NFT using this method
237
253
  mintParams.address
238
254
  .equals(this.address)
@@ -247,11 +263,16 @@ function CollectionFactory(params) {
247
263
  * @returns The MintEvent emitted.
248
264
  */
249
265
  async _mint(params) {
250
- const { name, address, data, metadata, storage, metadataVerificationKeyHash, expiry, } = params;
266
+ const { name, address, data, metadata, storage, metadataVerificationKeyHash, expiry, fee, tokenId, } = params;
251
267
  this.network.globalSlotSinceGenesis.requireBetween(UInt32.zero, expiry);
252
- data.version.assertEquals(UInt32.zero);
268
+ data.version.assertEquals(UInt64.zero);
269
+ data.isPaused
270
+ .equals(Bool(false))
271
+ .or(data.canPause.equals(Bool(true)))
272
+ .assertTrue(CollectionErrors.cannotMint);
253
273
  const packedData = data.pack();
254
- const tokenId = this.deriveTokenId();
274
+ const collectionTokenId = this.deriveTokenId();
275
+ collectionTokenId.assertEquals(tokenId);
255
276
  const update = AccountUpdate.createSigned(address, tokenId);
256
277
  update.body.useFullCommitment = Bool(true); // Prevent memo and fee change
257
278
  update.account.isNew.getAndRequireEquals().assertTrue();
@@ -279,17 +300,14 @@ function CollectionFactory(params) {
279
300
  });
280
301
  const mainnetVerificationKeyHash = Field(nftVerificationKeys.mainnet.vk.NFT.hash);
281
302
  const devnetVerificationKeyHash = Field(nftVerificationKeys.devnet.vk.NFT.hash);
282
- const isMainnet = Provable.witness(Bool, () => {
283
- // This check does NOT create a constraint on the verification key
284
- // as this witness can be replaced during runtime
285
- // and is useful only for making sure that the verification key
286
- // of the NFT will match the compiled verification key of the NFT
287
- // at the time of the deployment of the Collection Contract
288
- return Bool(Mina.getNetworkId() === "mainnet");
289
- });
290
303
  // We check that the verification key hash is the same as the one
291
- // that was compiled at the time of the deployment
292
- verificationKey.hash.assertEquals(Provable.if(isMainnet, mainnetVerificationKeyHash, devnetVerificationKeyHash));
304
+ // that was compiled at the time of the collection deployment
305
+ if (Mina.getNetworkId() === "mainnet") {
306
+ verificationKey.hash.assertEquals(mainnetVerificationKeyHash);
307
+ }
308
+ else {
309
+ verificationKey.hash.assertEquals(devnetVerificationKeyHash);
310
+ }
293
311
  update.body.update.verificationKey = {
294
312
  isSome: Bool(true),
295
313
  value: verificationKey,
@@ -323,6 +341,8 @@ function CollectionFactory(params) {
323
341
  const event = new MintEvent({
324
342
  initialState,
325
343
  address,
344
+ tokenId,
345
+ fee,
326
346
  });
327
347
  this.emitEvent("mint", event);
328
348
  return event;
@@ -334,6 +354,12 @@ function CollectionFactory(params) {
334
354
  * @param vk - The verification key.
335
355
  */
336
356
  async update(proof, vk) {
357
+ // The oracle address is optional and can be empty, NFT ZkProgram can verify the address
358
+ // as it can be different for different NFTs. It should be empty for the update() call
359
+ const oracleAddress = proof.publicInput.oracleAddress;
360
+ oracleAddress
361
+ .equals(PublicKey.empty())
362
+ .assertTrue(CollectionErrors.invalidOracleAddress);
337
363
  await this._update(proof, vk);
338
364
  }
339
365
  /**
@@ -344,7 +370,7 @@ function CollectionFactory(params) {
344
370
  */
345
371
  async updateWithOracle(proof, vk) {
346
372
  // The oracle address is optional and can be empty, NFT ZkProgram can verify the address
347
- // as it can be different for different NFTs
373
+ // as it can be different for different NFTs. It should be non-empty for the updateWithOracle() call
348
374
  const oracleAddress = proof.publicInput.oracleAddress;
349
375
  oracleAddress
350
376
  .equals(PublicKey.empty())
@@ -374,7 +400,9 @@ function CollectionFactory(params) {
374
400
  // Verify the metadata update proof
375
401
  metadataVerificationKeyHash.assertEquals(vk.hash);
376
402
  proof.verify(vk);
377
- this.emitEvent("update", proof.publicInput.immutableState.address);
403
+ this.emitEvent("update", new NFTUpdateEvent({
404
+ address: proof.publicInput.immutableState.address,
405
+ }));
378
406
  }
379
407
  /**
380
408
  * Approves an address to transfer an NFT.
@@ -393,10 +421,11 @@ function CollectionFactory(params) {
393
421
  /**
394
422
  * Transfers ownership of an NFT without admin approval.
395
423
  *
396
- * @param address - The address of the NFT.
397
- * @param to - The recipient's public key.
424
+ * @param nftAddress - The address of the NFT.
425
+ * @param approved - The approved public key.
398
426
  */
399
427
  async approveAddressByProof(nftAddress, approved) {
428
+ await this.ensureNotPaused();
400
429
  const tokenId = this.deriveTokenId();
401
430
  const nft = new NFT(nftAddress, tokenId);
402
431
  const owner = await nft.approveAddress(approved);
@@ -416,8 +445,7 @@ function CollectionFactory(params) {
416
445
  */
417
446
  async transferBySignature(params) {
418
447
  const { address, to, price, context } = params;
419
- const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
420
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
448
+ const collectionData = await this.ensureNotPaused();
421
449
  collectionData.requireTransferApproval.assertFalse(CollectionErrors.transferApprovalRequired);
422
450
  const transferEventDraft = new TransferExtendedParams({
423
451
  from: PublicKey.empty(), // will be added later
@@ -445,8 +473,8 @@ function CollectionFactory(params) {
445
473
  */
446
474
  async transferByProof(params) {
447
475
  const { address, from, to, price, context } = params;
448
- const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
449
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
476
+ const collectionData = await this.ensureNotPaused();
477
+ collectionData.requireTransferApproval.assertFalse(CollectionErrors.transferApprovalRequired);
450
478
  const transferEventDraft = new TransferExtendedParams({
451
479
  from,
452
480
  to,
@@ -482,10 +510,9 @@ function CollectionFactory(params) {
482
510
  *
483
511
  * @param params - The transfer parameters.
484
512
  */
485
- async approvedTransferByProof(params) {
513
+ async adminApprovedTransferByProof(params) {
486
514
  const { address, from, to, price, context } = params;
487
- const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
488
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
515
+ const collectionData = await this.ensureNotPaused();
489
516
  const transferEventDraft = new TransferExtendedParams({
490
517
  from,
491
518
  to,
@@ -524,10 +551,9 @@ function CollectionFactory(params) {
524
551
  * @param to - The recipient's public key.
525
552
  * @param price - The price of the NFT (optional).
526
553
  */
527
- async approvedTransferBySignature(params) {
554
+ async adminApprovedTransferBySignature(params) {
528
555
  const { address, to, price, context } = params;
529
- const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
530
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
556
+ const collectionData = await this.ensureNotPaused();
531
557
  const transferEventDraft = new TransferExtendedParams({
532
558
  from: PublicKey.empty(), // will be added later
533
559
  to,
@@ -547,14 +573,21 @@ function CollectionFactory(params) {
547
573
  const adminContract = this.getAdminContract();
548
574
  const canTransfer = await adminContract.canTransfer(transferEvent);
549
575
  canTransfer.assertTrue();
550
- this.emitEvent("transfer", transferEvent);
551
576
  }
552
577
  /**
553
578
  * Internal method to transfer an NFT.
554
579
  *
555
- * @param address - The address of the NFT.
556
- * @param to - The recipient's public key.
580
+ * This method handles the transfer logic and fee calculation. The fee is determined as follows:
581
+ * - If a price is provided, the fee is calculated as (price * royaltyFee / MAX_ROYALTY_FEE)
582
+ * - If no price is provided, the fixed transferFee is used to handle two cases:
583
+ * when NFT is being sold and the price is not provided to the contract
584
+ * when NFT is being transferred by the owner (without price)
585
+ * - If the sender is the creator, no fee is charged
586
+ * - The minimum fee is always the transferFee (unless sender is creator)
587
+ *
588
+ * @param transferEventDraft - The transfer event draft, containing the information about the transfer
557
589
  * @param transferFee - The transfer fee amount.
590
+ * @param royaltyFee - The royalty fee amount.
558
591
  * @returns The TransferEvent emitted.
559
592
  */
560
593
  async _transfer(params) {
@@ -567,7 +600,6 @@ function CollectionFactory(params) {
567
600
  const nft = new NFT(transferEventDraft.nft, tokenId);
568
601
  const transferEvent = await nft.transfer(transferEventDraft);
569
602
  const creator = this.creator.getAndRequireEquals();
570
- // TODO: check is the owner is the creator
571
603
  let fee = Provable.if(transferEventDraft.price.isSome,
572
604
  // We cannot check the price here, so we just rely on owner contract
573
605
  // Malicious owner contracts can be blocked by the admin contract
@@ -605,6 +637,7 @@ function CollectionFactory(params) {
605
637
  * @param vk - The new verification key.
606
638
  */
607
639
  async upgradeNFTVerificationKeyBySignature(address, vk) {
640
+ await this.ensureNotPaused();
608
641
  const sender = this.sender.getAndRequireSignature();
609
642
  const data = await this._upgrade(address, vk);
610
643
  data.owner
@@ -619,6 +652,7 @@ function CollectionFactory(params) {
619
652
  * @param vk - The new verification key.
620
653
  */
621
654
  async upgradeNFTVerificationKeyByProof(address, vk) {
655
+ await this.ensureNotPaused();
622
656
  const data = await this._upgrade(address, vk);
623
657
  const ownerContract = this.getOwnerContract(data.owner);
624
658
  const canUpgrade = await ownerContract.canChangeVerificationKey(this.address, address, vk);
@@ -645,19 +679,23 @@ function CollectionFactory(params) {
645
679
  * @param vk - The new verification key.
646
680
  */
647
681
  async upgradeVerificationKey(vk) {
682
+ await this.ensureNotPaused();
648
683
  const adminContract = this.getAdminContract();
649
684
  const canUpgrade = await adminContract.canChangeVerificationKey(vk, this.address, this.tokenId);
650
685
  canUpgrade.assertTrue(CollectionErrors.cannotUpgradeVerificationKey);
651
686
  this.account.verificationKey.set(vk);
652
- this.emitEvent("upgradeVerificationKey", vk.hash);
687
+ this.emitEvent("upgradeVerificationKey", new UpgradeVerificationKeyEvent({
688
+ address: this.address,
689
+ tokenId: this.tokenId,
690
+ verificationKeyHash: vk.hash,
691
+ }));
653
692
  }
654
693
  /**
655
694
  * Limits further minting of NFTs in the collection.
656
695
  */
657
696
  async limitMinting() {
658
697
  await this.ensureCreatorSignature();
659
- const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
660
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
698
+ const collectionData = await this.ensureNotPaused();
661
699
  collectionData.mintingIsLimited = Bool(true);
662
700
  this.packedData.set(collectionData.pack());
663
701
  this.emitEvent("limitMinting", new LimitMintingEvent({ mintingLimited: Bool(true) }));
@@ -666,8 +704,7 @@ function CollectionFactory(params) {
666
704
  * Pauses the collection, disabling certain actions.
667
705
  */
668
706
  async pause() {
669
- const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
670
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
707
+ const collectionData = await this.ensureNotPaused();
671
708
  const adminContract = this.getAdminContract();
672
709
  const canPause = await adminContract.canPause();
673
710
  canPause.assertTrue(CollectionErrors.noPermissionToPause);
@@ -694,6 +731,7 @@ function CollectionFactory(params) {
694
731
  * @param address - The address of the NFT to pause.
695
732
  */
696
733
  async pauseNFTBySignature(address) {
734
+ await this.ensureNotPaused();
697
735
  const tokenId = this.deriveTokenId();
698
736
  const nft = new NFT(address, tokenId);
699
737
  const owner = await nft.pause();
@@ -706,6 +744,7 @@ function CollectionFactory(params) {
706
744
  * @param address - The address of the NFT to pause.
707
745
  */
708
746
  async pauseNFTByProof(address) {
747
+ await this.ensureNotPaused();
709
748
  const tokenId = this.deriveTokenId();
710
749
  const nft = new NFT(address, tokenId);
711
750
  const owner = await nft.pause();
@@ -720,6 +759,7 @@ function CollectionFactory(params) {
720
759
  * @param address - The address of the NFT to resume.
721
760
  */
722
761
  async resumeNFT(address) {
762
+ await this.ensureNotPaused();
723
763
  const tokenId = this.deriveTokenId();
724
764
  const nft = new NFT(address, tokenId);
725
765
  const owner = await nft.resume();
@@ -732,6 +772,7 @@ function CollectionFactory(params) {
732
772
  * @param address - The address of the NFT to resume.
733
773
  */
734
774
  async resumeNFTByProof(address) {
775
+ await this.ensureNotPaused();
735
776
  const tokenId = this.deriveTokenId();
736
777
  const nft = new NFT(address, tokenId);
737
778
  const owner = await nft.resume();
@@ -754,7 +795,7 @@ function CollectionFactory(params) {
754
795
  const canChangeName = await adminContract.canChangeName(name);
755
796
  canChangeName.assertTrue(CollectionErrors.noPermissionToChangeName);
756
797
  this.collectionName.set(name);
757
- this.emitEvent("setName", name);
798
+ this.emitEvent("setName", new SetNameEvent({ name }));
758
799
  }
759
800
  /**
760
801
  * Updates the base URL for the collection's metadata.
@@ -770,7 +811,7 @@ function CollectionFactory(params) {
770
811
  const canChangeBaseUri = await adminContract.canChangeBaseUri(baseURL);
771
812
  canChangeBaseUri.assertTrue(CollectionErrors.noPermissionToChangeBaseUri);
772
813
  this.baseURL.set(baseURL);
773
- this.emitEvent("setBaseURL", baseURL);
814
+ this.emitEvent("setBaseURL", new SetBaseURLEvent({ baseURL }));
774
815
  }
775
816
  /**
776
817
  * Sets a new admin address for the collection.
@@ -786,7 +827,7 @@ function CollectionFactory(params) {
786
827
  const canSetAdmin = await adminContract.canSetAdmin(admin);
787
828
  canSetAdmin.assertTrue(CollectionErrors.noPermissionToSetAdmin);
788
829
  this.admin.set(admin);
789
- this.emitEvent("setAdmin", admin);
830
+ this.emitEvent("setAdmin", new SetAdminEvent({ admin }));
790
831
  }
791
832
  /**
792
833
  * Updates the royalty fee for the collection.
@@ -797,15 +838,14 @@ function CollectionFactory(params) {
797
838
  * @throws {Error} If caller lacks permission to change royalty fee
798
839
  */
799
840
  async setRoyaltyFee(royaltyFee) {
800
- const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
801
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
841
+ const collectionData = await this.ensureNotPaused();
802
842
  royaltyFee.assertLessThanOrEqual(UInt32.from(MAX_ROYALTY_FEE), CollectionErrors.invalidRoyaltyFee);
803
843
  const adminContract = this.getAdminContract();
804
844
  const canChangeRoyalty = await adminContract.canChangeRoyalty(royaltyFee);
805
845
  canChangeRoyalty.assertTrue(CollectionErrors.noPermissionToChangeRoyalty);
806
846
  collectionData.royaltyFee = royaltyFee;
807
847
  this.packedData.set(collectionData.pack());
808
- this.emitEvent("setRoyaltyFee", royaltyFee);
848
+ this.emitEvent("setRoyaltyFee", new SetRoyaltyFeeEvent({ royaltyFee }));
809
849
  }
810
850
  /**
811
851
  * Updates the transfer fee for the collection.
@@ -816,35 +856,76 @@ function CollectionFactory(params) {
816
856
  * @throws {Error} If caller lacks permission to change transfer fee
817
857
  */
818
858
  async setTransferFee(transferFee) {
819
- const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
820
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
859
+ const collectionData = await this.ensureNotPaused();
821
860
  const adminContract = this.getAdminContract();
822
861
  const canChangeTransferFee = await adminContract.canChangeTransferFee(transferFee);
823
862
  canChangeTransferFee.assertTrue(CollectionErrors.noPermissionToChangeTransferFee);
824
863
  collectionData.transferFee = transferFee;
825
864
  this.packedData.set(collectionData.pack());
826
- this.emitEvent("setTransferFee", transferFee);
865
+ this.emitEvent("setTransferFee", new SetTransferFeeEvent({ transferFee }));
827
866
  }
828
867
  /**
829
- * Transfers ownership of the collection to a new owner.
868
+ * Transfers ownership of the collection to a new creator.
869
+ * This method is called transferOwnership as the Collection is implementing OwnableContract interface
870
+ * For the Collection, the creator is the owner of the collection
830
871
  *
831
- * @param to - The public key of the new owner.
832
- * @returns The public key of the old owner.
872
+ * @param to - The public key of the new creator.
873
+ * @returns The public key of the old creator.
833
874
  */
834
875
  async transferOwnership(to) {
835
876
  await this.ensureCreatorSignature();
836
- await this.ensureNotPaused();
877
+ const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
878
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionNotPaused);
837
879
  const adminContract = this.getAdminContract();
838
880
  const canChangeCreator = await adminContract.canChangeCreator(to);
839
881
  canChangeCreator.assertTrue(CollectionErrors.noPermissionToChangeCreator);
840
882
  const from = this.creator.getAndRequireEquals();
841
- this.creator.set(to);
842
- this.emitEvent("ownershipChange", new OwnershipChangeEvent({
883
+ // Pending creator public key can be empty, it cancels the transfer
884
+ this.pendingCreatorX.set(to.x);
885
+ collectionData.pendingCreatorIsOdd = to.isOdd;
886
+ this.packedData.set(collectionData.pack());
887
+ this.emitEvent("ownershipTransfer", new OwnershipChangeEvent({
843
888
  from,
844
889
  to,
845
890
  }));
846
891
  return from;
847
892
  }
893
+ /**
894
+ * Transfers ownership of the collection to a new owner.
895
+ *
896
+ * @param to - The public key of the new owner.
897
+ * @returns The public key of the old owner.
898
+ */
899
+ async acceptOwnership() {
900
+ const collectionData = CollectionData.unpack(this.packedData.getAndRequireEquals());
901
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionNotPaused);
902
+ const pendingCreatorX = this.pendingCreatorX.getAndRequireEquals();
903
+ const pendingCreator = PublicKey.from({
904
+ x: pendingCreatorX,
905
+ isOdd: collectionData.pendingCreatorIsOdd,
906
+ });
907
+ const emptyPublicKey = PublicKey.empty();
908
+ pendingCreator
909
+ .equals(emptyPublicKey)
910
+ .assertFalse(CollectionErrors.pendingCreatorIsEmpty);
911
+ // pendingCreator can be different from the sender, but it should sign the tx
912
+ const pendingCreatorUpdate = AccountUpdate.createSigned(pendingCreator);
913
+ pendingCreatorUpdate.body.useFullCommitment = Bool(true); // Prevent memo and fee change
914
+ // Check second time that the transfer is allowed
915
+ const adminContract = this.getAdminContract();
916
+ const canChangeCreator = await adminContract.canChangeCreator(pendingCreator);
917
+ canChangeCreator.assertTrue(CollectionErrors.noPermissionToChangeCreator);
918
+ const from = this.creator.getAndRequireEquals();
919
+ this.pendingCreatorX.set(emptyPublicKey.x);
920
+ collectionData.pendingCreatorIsOdd = Bool(emptyPublicKey.isOdd);
921
+ this.creator.set(pendingCreator);
922
+ this.packedData.set(collectionData.pack());
923
+ this.emitEvent("ownershipAccepted", new OwnershipChangeEvent({
924
+ from,
925
+ to: pendingCreator,
926
+ }));
927
+ return from;
928
+ }
848
929
  async getNFTState(address) {
849
930
  const tokenId = this.deriveTokenId();
850
931
  const nft = new NFT(address, tokenId);
@@ -872,6 +953,10 @@ function CollectionFactory(params) {
872
953
  state(Field),
873
954
  __metadata("design:type", Object)
874
955
  ], Collection.prototype, "packedData", void 0);
956
+ __decorate([
957
+ state(Field),
958
+ __metadata("design:type", Object)
959
+ ], Collection.prototype, "pendingCreatorX", void 0);
875
960
  __decorate([
876
961
  method,
877
962
  __metadata("design:type", Function),
@@ -921,27 +1006,27 @@ function CollectionFactory(params) {
921
1006
  __decorate([
922
1007
  method,
923
1008
  __metadata("design:type", Function),
924
- __metadata("design:paramtypes", [TransferParams]),
1009
+ __metadata("design:paramtypes", [TransferBySignatureParams]),
925
1010
  __metadata("design:returntype", Promise)
926
1011
  ], Collection.prototype, "transferBySignature", null);
927
1012
  __decorate([
928
1013
  method,
929
1014
  __metadata("design:type", Function),
930
- __metadata("design:paramtypes", [TransferParams]),
1015
+ __metadata("design:paramtypes", [TransferByProofParams]),
931
1016
  __metadata("design:returntype", Promise)
932
1017
  ], Collection.prototype, "transferByProof", null);
933
1018
  __decorate([
934
1019
  method,
935
1020
  __metadata("design:type", Function),
936
- __metadata("design:paramtypes", [TransferParams]),
1021
+ __metadata("design:paramtypes", [TransferByProofParams]),
937
1022
  __metadata("design:returntype", Promise)
938
- ], Collection.prototype, "approvedTransferByProof", null);
1023
+ ], Collection.prototype, "adminApprovedTransferByProof", null);
939
1024
  __decorate([
940
1025
  method,
941
1026
  __metadata("design:type", Function),
942
- __metadata("design:paramtypes", [TransferParams]),
1027
+ __metadata("design:paramtypes", [TransferBySignatureParams]),
943
1028
  __metadata("design:returntype", Promise)
944
- ], Collection.prototype, "approvedTransferBySignature", null);
1029
+ ], Collection.prototype, "adminApprovedTransferBySignature", null);
945
1030
  __decorate([
946
1031
  method,
947
1032
  __metadata("design:type", Function),
@@ -1040,6 +1125,12 @@ function CollectionFactory(params) {
1040
1125
  __metadata("design:paramtypes", [PublicKey]),
1041
1126
  __metadata("design:returntype", Promise)
1042
1127
  ], Collection.prototype, "transferOwnership", null);
1128
+ __decorate([
1129
+ method.returns(PublicKey),
1130
+ __metadata("design:type", Function),
1131
+ __metadata("design:paramtypes", []),
1132
+ __metadata("design:returntype", Promise)
1133
+ ], Collection.prototype, "acceptOwnership", null);
1043
1134
  __decorate([
1044
1135
  method.returns(NFTStateStruct),
1045
1136
  __metadata("design:type", Function),