@silvana-one/nft 0.2.11 → 1.0.0

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 (81) hide show
  1. package/README.md +12 -0
  2. package/dist/node/contracts/admin.d.ts +100 -10
  3. package/dist/node/contracts/admin.js +130 -29
  4. package/dist/node/contracts/admin.js.map +1 -1
  5. package/dist/node/contracts/collection.d.ts +51 -34
  6. package/dist/node/contracts/collection.js +162 -71
  7. package/dist/node/contracts/collection.js.map +1 -1
  8. package/dist/node/contracts/nft.js +3 -0
  9. package/dist/node/contracts/nft.js.map +1 -1
  10. package/dist/node/contracts.d.ts +36 -30
  11. package/dist/node/index.cjs +447 -182
  12. package/dist/node/interfaces/collection.d.ts +5 -5
  13. package/dist/node/interfaces/events.d.ts +306 -16
  14. package/dist/node/interfaces/events.js +42 -4
  15. package/dist/node/interfaces/events.js.map +1 -1
  16. package/dist/node/interfaces/ownable.d.ts +7 -7
  17. package/dist/node/interfaces/ownable.js.map +1 -1
  18. package/dist/node/interfaces/pausable.d.ts +3 -3
  19. package/dist/node/interfaces/pausable.js.map +1 -1
  20. package/dist/node/interfaces/types.d.ts +182 -27
  21. package/dist/node/interfaces/types.js +120 -71
  22. package/dist/node/interfaces/types.js.map +1 -1
  23. package/dist/node/marketplace/auction.d.ts +2 -2
  24. package/dist/node/marketplace/auction.js +4 -4
  25. package/dist/node/marketplace/auction.js.map +1 -1
  26. package/dist/node/marketplace/bid.js +3 -5
  27. package/dist/node/marketplace/bid.js.map +1 -1
  28. package/dist/node/marketplace/nft-shares.d.ts +2 -2
  29. package/dist/node/util/div.js +10 -5
  30. package/dist/node/util/div.js.map +1 -1
  31. package/dist/node/vk.js +6 -6
  32. package/dist/node/vk.js.map +1 -1
  33. package/dist/node/zkprogram-example/game.d.ts +2 -2
  34. package/dist/node/zkprogram-example/update.d.ts +2 -2
  35. package/dist/tsconfig.node.tsbuildinfo +1 -1
  36. package/dist/tsconfig.web.tsbuildinfo +1 -1
  37. package/dist/web/contracts/admin.d.ts +100 -10
  38. package/dist/web/contracts/admin.js +130 -29
  39. package/dist/web/contracts/admin.js.map +1 -1
  40. package/dist/web/contracts/collection.d.ts +51 -34
  41. package/dist/web/contracts/collection.js +162 -71
  42. package/dist/web/contracts/collection.js.map +1 -1
  43. package/dist/web/contracts/nft.js +3 -0
  44. package/dist/web/contracts/nft.js.map +1 -1
  45. package/dist/web/contracts.d.ts +36 -30
  46. package/dist/web/interfaces/collection.d.ts +5 -5
  47. package/dist/web/interfaces/events.d.ts +306 -16
  48. package/dist/web/interfaces/events.js +42 -4
  49. package/dist/web/interfaces/events.js.map +1 -1
  50. package/dist/web/interfaces/ownable.d.ts +7 -7
  51. package/dist/web/interfaces/ownable.js.map +1 -1
  52. package/dist/web/interfaces/pausable.d.ts +3 -3
  53. package/dist/web/interfaces/pausable.js.map +1 -1
  54. package/dist/web/interfaces/types.d.ts +182 -27
  55. package/dist/web/interfaces/types.js +120 -71
  56. package/dist/web/interfaces/types.js.map +1 -1
  57. package/dist/web/marketplace/auction.d.ts +2 -2
  58. package/dist/web/marketplace/auction.js +4 -4
  59. package/dist/web/marketplace/auction.js.map +1 -1
  60. package/dist/web/marketplace/bid.js +3 -5
  61. package/dist/web/marketplace/bid.js.map +1 -1
  62. package/dist/web/marketplace/nft-shares.d.ts +2 -2
  63. package/dist/web/util/div.js +10 -5
  64. package/dist/web/util/div.js.map +1 -1
  65. package/dist/web/vk.js +6 -6
  66. package/dist/web/vk.js.map +1 -1
  67. package/dist/web/zkprogram-example/game.d.ts +2 -2
  68. package/dist/web/zkprogram-example/update.d.ts +2 -2
  69. package/package.json +8 -8
  70. package/src/contracts/admin.ts +137 -24
  71. package/src/contracts/collection.ts +188 -96
  72. package/src/contracts/nft.ts +3 -0
  73. package/src/interfaces/collection.ts +11 -5
  74. package/src/interfaces/events.ts +47 -3
  75. package/src/interfaces/ownable.ts +1 -1
  76. package/src/interfaces/pausable.ts +1 -1
  77. package/src/interfaces/types.ts +152 -78
  78. package/src/marketplace/auction.ts +5 -4
  79. package/src/marketplace/bid.ts +4 -6
  80. package/src/util/div.ts +12 -5
  81. package/src/vk.ts +6 -6
@@ -21,7 +21,6 @@ import {
21
21
  VerificationKey,
22
22
  UInt32,
23
23
  UInt64,
24
- SmartContract,
25
24
  Mina,
26
25
  Provable,
27
26
  } from "o1js";
@@ -29,11 +28,13 @@ import { NFT } from "./nft.js";
29
28
  import {
30
29
  MintParams,
31
30
  MintRequest,
32
- TransferParams,
31
+ TransferBySignatureParams,
32
+ TransferByProofParams,
33
33
  CollectionData,
34
34
  NFTUpdateProof,
35
35
  NFTStateStruct,
36
36
  MintEvent,
37
+ NFTUpdateEvent,
37
38
  TransferEvent,
38
39
  ApproveEvent,
39
40
  UpgradeVerificationKeyEvent,
@@ -43,6 +44,11 @@ import {
43
44
  NFTAdminContractConstructor,
44
45
  PausableContract,
45
46
  PauseEvent,
47
+ SetNameEvent,
48
+ SetBaseURLEvent,
49
+ SetRoyaltyFeeEvent,
50
+ SetTransferFeeEvent,
51
+ SetAdminEvent,
46
52
  OwnableContract,
47
53
  OwnershipChangeEvent,
48
54
  NFTOwnerBase,
@@ -85,6 +91,7 @@ const CollectionErrors = {
85
91
  onlyOwnerCanUpgradeVerificationKey: "Only owner can upgrade verification key",
86
92
  invalidRoyaltyFee: "Royalty fee is too high, cannot be more than 100%",
87
93
  invalidOracleAddress: "Oracle address is invalid",
94
+ pendingCreatorIsEmpty: "Pending creator address is empty",
88
95
  };
89
96
 
90
97
  interface CollectionDeployProps extends Exclude<DeployArgs, undefined> {
@@ -132,6 +139,8 @@ function CollectionFactory(params: {
132
139
  * such as flags and fee configurations.
133
140
  */
134
141
  @state(Field) packedData = State<Field>();
142
+ /** The public key part (x) of the pending creator. The isOdd field is written to the packedData */
143
+ @state(Field) pendingCreatorX = State<Field>();
135
144
 
136
145
  /**
137
146
  * Deploys the NFT Collection Contract with the initial settings.
@@ -150,6 +159,9 @@ function CollectionFactory(params: {
150
159
  isPaused: true,
151
160
  }).pack()
152
161
  );
162
+ this.pendingCreatorX.set(PublicKey.empty().x);
163
+ // Changes must be made if the number of state fields available on the Mina blockchain changes
164
+ // This function should initialize ALL state fields due to the logic in the initialize() method
153
165
  this.account.zkappUri.set(props.url);
154
166
  this.account.tokenSymbol.set(props.symbol);
155
167
  this.account.permissions.set({
@@ -159,8 +171,8 @@ function CollectionFactory(params: {
159
171
  setPermissions: Permissions.impossible(),
160
172
  access: Permissions.proof(),
161
173
  send: Permissions.proof(),
162
- setZkappUri: Permissions.none(),
163
- setTokenSymbol: Permissions.none(),
174
+ setZkappUri: Permissions.proof(),
175
+ setTokenSymbol: Permissions.proof(),
164
176
  });
165
177
  }
166
178
 
@@ -172,6 +184,9 @@ function CollectionFactory(params: {
172
184
  */
173
185
  @method
174
186
  async initialize(masterNFT: MintParams, collectionData: CollectionData) {
187
+ // Changes must be made if the number of state fields available on the Mina blockchain changes
188
+ // as the next line relies on the fact that the state size is 8 Fields
189
+ // and all 8 Field are initialized in deploy()
175
190
  this.account.provedState.requireEquals(Bool(false));
176
191
  collectionData.royaltyFee.assertLessThanOrEqual(
177
192
  UInt32.from(MAX_ROYALTY_FEE),
@@ -189,22 +204,23 @@ function CollectionFactory(params: {
189
204
  */
190
205
  events = {
191
206
  mint: MintEvent,
192
- update: PublicKey,
207
+ update: NFTUpdateEvent,
193
208
  transfer: TransferEvent,
194
209
  approve: ApproveEvent,
195
210
  upgradeNFTVerificationKey: UpgradeVerificationKeyEvent,
196
- upgradeVerificationKey: Field,
211
+ upgradeVerificationKey: UpgradeVerificationKeyEvent,
197
212
  limitMinting: LimitMintingEvent,
198
213
  pause: PauseEvent,
199
214
  resume: PauseEvent,
200
215
  pauseNFT: PauseNFTEvent,
201
216
  resumeNFT: PauseNFTEvent,
202
- ownershipChange: OwnershipChangeEvent,
203
- setName: Field,
204
- setBaseURL: Field,
205
- setRoyaltyFee: UInt32,
206
- setTransferFee: UInt64,
207
- setAdmin: PublicKey,
217
+ ownershipTransfer: OwnershipChangeEvent,
218
+ ownershipAccepted: OwnershipChangeEvent,
219
+ setName: SetNameEvent,
220
+ setBaseURL: SetBaseURLEvent,
221
+ setRoyaltyFee: SetRoyaltyFeeEvent,
222
+ setTransferFee: SetTransferFeeEvent,
223
+ setAdmin: SetAdminEvent,
208
224
  };
209
225
 
210
226
  /**
@@ -285,10 +301,12 @@ function CollectionFactory(params: {
285
301
  *
286
302
  * @returns The packed data of the collection.
287
303
  */
288
- async ensureNotPaused(): Promise<void> {
289
- CollectionData.isPaused(
304
+ async ensureNotPaused(): Promise<CollectionData> {
305
+ const collectionData = CollectionData.unpack(
290
306
  this.packedData.getAndRequireEquals()
291
- ).assertFalse(CollectionErrors.collectionPaused);
307
+ );
308
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
309
+ return collectionData;
292
310
  }
293
311
 
294
312
  /**
@@ -307,9 +325,8 @@ function CollectionFactory(params: {
307
325
  */
308
326
 
309
327
  @method async mintByCreator(params: MintParams): Promise<void> {
310
- CollectionData.mintingIsLimited(
311
- this.packedData.getAndRequireEquals()
312
- ).assertFalse(CollectionErrors.cannotMint);
328
+ const collectionData = await this.ensureNotPaused();
329
+ collectionData.mintingIsLimited.assertFalse(CollectionErrors.cannotMint);
313
330
  const creatorUpdate = await this.ensureCreatorSignature();
314
331
  // Pay 1 MINA fee for a new account
315
332
  creatorUpdate.balance.subInPlace(1_000_000_000);
@@ -322,14 +339,15 @@ function CollectionFactory(params: {
322
339
  * @param mintRequest - The minting request containing parameters and proofs.
323
340
  */
324
341
  @method async mint(mintRequest: MintRequest): Promise<void> {
325
- CollectionData.mintingIsLimited(
326
- this.packedData.getAndRequireEquals()
327
- ).assertFalse(CollectionErrors.cannotMint);
342
+ const collectionData = await this.ensureNotPaused();
343
+ collectionData.mintingIsLimited.assertFalse(CollectionErrors.cannotMint);
328
344
  const adminContract = this.getAdminContract();
329
345
  // The admin contract checks that the sender is allowed to mint
330
346
  const mintParams = (await adminContract.canMint(mintRequest)).assertSome(
331
347
  CollectionErrors.cannotMint
332
348
  );
349
+ mintParams.address.assertEquals(mintRequest.address);
350
+ mintParams.data.owner.assertEquals(mintRequest.owner);
333
351
 
334
352
  // Prevent minting the Master NFT using this method
335
353
  mintParams.address
@@ -354,12 +372,19 @@ function CollectionFactory(params: {
354
372
  storage,
355
373
  metadataVerificationKeyHash,
356
374
  expiry,
375
+ fee,
376
+ tokenId,
357
377
  } = params;
358
378
 
359
379
  this.network.globalSlotSinceGenesis.requireBetween(UInt32.zero, expiry);
360
- data.version.assertEquals(UInt32.zero);
380
+ data.version.assertEquals(UInt64.zero);
381
+ data.isPaused
382
+ .equals(Bool(false))
383
+ .or(data.canPause.equals(Bool(true)))
384
+ .assertTrue(CollectionErrors.cannotMint);
361
385
  const packedData = data.pack();
362
- const tokenId = this.deriveTokenId();
386
+ const collectionTokenId = this.deriveTokenId();
387
+ collectionTokenId.assertEquals(tokenId);
363
388
 
364
389
  const update = AccountUpdate.createSigned(address, tokenId);
365
390
  update.body.useFullCommitment = Bool(true); // Prevent memo and fee change
@@ -405,23 +430,13 @@ function CollectionFactory(params: {
405
430
  const devnetVerificationKeyHash = Field(
406
431
  nftVerificationKeys.devnet.vk.NFT.hash
407
432
  );
408
- const isMainnet = Provable.witness(Bool, () => {
409
- // This check does NOT create a constraint on the verification key
410
- // as this witness can be replaced during runtime
411
- // and is useful only for making sure that the verification key
412
- // of the NFT will match the compiled verification key of the NFT
413
- // at the time of the deployment of the Collection Contract
414
- return Bool(Mina.getNetworkId() === "mainnet");
415
- });
416
433
  // We check that the verification key hash is the same as the one
417
- // that was compiled at the time of the deployment
418
- verificationKey.hash.assertEquals(
419
- Provable.if(
420
- isMainnet,
421
- mainnetVerificationKeyHash,
422
- devnetVerificationKeyHash
423
- )
424
- );
434
+ // that was compiled at the time of the collection deployment
435
+ if (Mina.getNetworkId() === "mainnet") {
436
+ verificationKey.hash.assertEquals(mainnetVerificationKeyHash);
437
+ } else {
438
+ verificationKey.hash.assertEquals(devnetVerificationKeyHash);
439
+ }
425
440
  update.body.update.verificationKey = {
426
441
  isSome: Bool(true),
427
442
  value: verificationKey,
@@ -459,6 +474,8 @@ function CollectionFactory(params: {
459
474
  const event = new MintEvent({
460
475
  initialState,
461
476
  address,
477
+ tokenId,
478
+ fee,
462
479
  });
463
480
  this.emitEvent("mint", event);
464
481
  return event;
@@ -474,6 +491,12 @@ function CollectionFactory(params: {
474
491
  proof: NFTUpdateProof,
475
492
  vk: VerificationKey
476
493
  ): Promise<void> {
494
+ // The oracle address is optional and can be empty, NFT ZkProgram can verify the address
495
+ // as it can be different for different NFTs. It should be empty for the update() call
496
+ const oracleAddress = proof.publicInput.oracleAddress;
497
+ oracleAddress
498
+ .equals(PublicKey.empty())
499
+ .assertTrue(CollectionErrors.invalidOracleAddress);
477
500
  await this._update(proof, vk);
478
501
  }
479
502
 
@@ -488,7 +511,7 @@ function CollectionFactory(params: {
488
511
  vk: VerificationKey
489
512
  ): Promise<void> {
490
513
  // The oracle address is optional and can be empty, NFT ZkProgram can verify the address
491
- // as it can be different for different NFTs
514
+ // as it can be different for different NFTs. It should be non-empty for the updateWithOracle() call
492
515
  const oracleAddress = proof.publicInput.oracleAddress;
493
516
  oracleAddress
494
517
  .equals(PublicKey.empty())
@@ -535,7 +558,12 @@ function CollectionFactory(params: {
535
558
  // Verify the metadata update proof
536
559
  metadataVerificationKeyHash.assertEquals(vk.hash);
537
560
  proof.verify(vk);
538
- this.emitEvent("update", proof.publicInput.immutableState.address);
561
+ this.emitEvent(
562
+ "update",
563
+ new NFTUpdateEvent({
564
+ address: proof.publicInput.immutableState.address,
565
+ })
566
+ );
539
567
  }
540
568
 
541
569
  /**
@@ -559,13 +587,14 @@ function CollectionFactory(params: {
559
587
  /**
560
588
  * Transfers ownership of an NFT without admin approval.
561
589
  *
562
- * @param address - The address of the NFT.
563
- * @param to - The recipient's public key.
590
+ * @param nftAddress - The address of the NFT.
591
+ * @param approved - The approved public key.
564
592
  */
565
593
  @method async approveAddressByProof(
566
594
  nftAddress: PublicKey,
567
595
  approved: PublicKey
568
596
  ): Promise<void> {
597
+ await this.ensureNotPaused();
569
598
  const tokenId = this.deriveTokenId();
570
599
  const nft = new NFT(nftAddress, tokenId);
571
600
  const owner = await nft.approveAddress(approved);
@@ -588,12 +617,11 @@ function CollectionFactory(params: {
588
617
  * @param to - The recipient's public key.
589
618
  * @param price - The price of the NFT (optional).
590
619
  */
591
- @method async transferBySignature(params: TransferParams): Promise<void> {
620
+ @method async transferBySignature(
621
+ params: TransferBySignatureParams
622
+ ): Promise<void> {
592
623
  const { address, to, price, context } = params;
593
- const collectionData = CollectionData.unpack(
594
- this.packedData.getAndRequireEquals()
595
- );
596
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
624
+ const collectionData = await this.ensureNotPaused();
597
625
  collectionData.requireTransferApproval.assertFalse(
598
626
  CollectionErrors.transferApprovalRequired
599
627
  );
@@ -623,12 +651,14 @@ function CollectionFactory(params: {
623
651
  *
624
652
  * @param params - The transfer parameters.
625
653
  */
626
- @method async transferByProof(params: TransferParams): Promise<void> {
654
+ @method async transferByProof(
655
+ params: TransferByProofParams
656
+ ): Promise<void> {
627
657
  const { address, from, to, price, context } = params;
628
- const collectionData = CollectionData.unpack(
629
- this.packedData.getAndRequireEquals()
658
+ const collectionData = await this.ensureNotPaused();
659
+ collectionData.requireTransferApproval.assertFalse(
660
+ CollectionErrors.transferApprovalRequired
630
661
  );
631
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
632
662
 
633
663
  const transferEventDraft = new TransferExtendedParams({
634
664
  from,
@@ -666,14 +696,11 @@ function CollectionFactory(params: {
666
696
  *
667
697
  * @param params - The transfer parameters.
668
698
  */
669
- @method async approvedTransferByProof(
670
- params: TransferParams
699
+ @method async adminApprovedTransferByProof(
700
+ params: TransferByProofParams
671
701
  ): Promise<void> {
672
702
  const { address, from, to, price, context } = params;
673
- const collectionData = CollectionData.unpack(
674
- this.packedData.getAndRequireEquals()
675
- );
676
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
703
+ const collectionData = await this.ensureNotPaused();
677
704
 
678
705
  const transferEventDraft = new TransferExtendedParams({
679
706
  from,
@@ -718,14 +745,11 @@ function CollectionFactory(params: {
718
745
  * @param to - The recipient's public key.
719
746
  * @param price - The price of the NFT (optional).
720
747
  */
721
- @method async approvedTransferBySignature(
722
- params: TransferParams
748
+ @method async adminApprovedTransferBySignature(
749
+ params: TransferBySignatureParams
723
750
  ): Promise<void> {
724
751
  const { address, to, price, context } = params;
725
- const collectionData = CollectionData.unpack(
726
- this.packedData.getAndRequireEquals()
727
- );
728
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
752
+ const collectionData = await this.ensureNotPaused();
729
753
 
730
754
  const transferEventDraft = new TransferExtendedParams({
731
755
  from: PublicKey.empty(), // will be added later
@@ -746,15 +770,22 @@ function CollectionFactory(params: {
746
770
  const adminContract = this.getAdminContract();
747
771
  const canTransfer = await adminContract.canTransfer(transferEvent);
748
772
  canTransfer.assertTrue();
749
- this.emitEvent("transfer", transferEvent);
750
773
  }
751
774
 
752
775
  /**
753
776
  * Internal method to transfer an NFT.
754
777
  *
755
- * @param address - The address of the NFT.
756
- * @param to - The recipient's public key.
778
+ * This method handles the transfer logic and fee calculation. The fee is determined as follows:
779
+ * - If a price is provided, the fee is calculated as (price * royaltyFee / MAX_ROYALTY_FEE)
780
+ * - If no price is provided, the fixed transferFee is used to handle two cases:
781
+ * when NFT is being sold and the price is not provided to the contract
782
+ * when NFT is being transferred by the owner (without price)
783
+ * - If the sender is the creator, no fee is charged
784
+ * - The minimum fee is always the transferFee (unless sender is creator)
785
+ *
786
+ * @param transferEventDraft - The transfer event draft, containing the information about the transfer
757
787
  * @param transferFee - The transfer fee amount.
788
+ * @param royaltyFee - The royalty fee amount.
758
789
  * @returns The TransferEvent emitted.
759
790
  */
760
791
  async _transfer(params: {
@@ -776,7 +807,6 @@ function CollectionFactory(params: {
776
807
  const nft = new NFT(transferEventDraft.nft, tokenId);
777
808
  const transferEvent = await nft.transfer(transferEventDraft);
778
809
  const creator = this.creator.getAndRequireEquals();
779
- // TODO: check is the owner is the creator
780
810
  let fee = Provable.if(
781
811
  transferEventDraft.price.isSome,
782
812
  // We cannot check the price here, so we just rely on owner contract
@@ -834,6 +864,7 @@ function CollectionFactory(params: {
834
864
  address: PublicKey,
835
865
  vk: VerificationKey
836
866
  ): Promise<void> {
867
+ await this.ensureNotPaused();
837
868
  const sender = this.sender.getAndRequireSignature();
838
869
  const data = await this._upgrade(address, vk);
839
870
  data.owner
@@ -853,6 +884,7 @@ function CollectionFactory(params: {
853
884
  address: PublicKey,
854
885
  vk: VerificationKey
855
886
  ): Promise<void> {
887
+ await this.ensureNotPaused();
856
888
  const data = await this._upgrade(address, vk);
857
889
  const ownerContract = this.getOwnerContract(data.owner);
858
890
  const canUpgrade = await ownerContract.canChangeVerificationKey(
@@ -895,6 +927,7 @@ function CollectionFactory(params: {
895
927
  */
896
928
  @method
897
929
  async upgradeVerificationKey(vk: VerificationKey): Promise<void> {
930
+ await this.ensureNotPaused();
898
931
  const adminContract = this.getAdminContract();
899
932
  const canUpgrade = await adminContract.canChangeVerificationKey(
900
933
  vk,
@@ -904,7 +937,14 @@ function CollectionFactory(params: {
904
937
  canUpgrade.assertTrue(CollectionErrors.cannotUpgradeVerificationKey);
905
938
  this.account.verificationKey.set(vk);
906
939
 
907
- this.emitEvent("upgradeVerificationKey", vk.hash);
940
+ this.emitEvent(
941
+ "upgradeVerificationKey",
942
+ new UpgradeVerificationKeyEvent({
943
+ address: this.address,
944
+ tokenId: this.tokenId,
945
+ verificationKeyHash: vk.hash,
946
+ })
947
+ );
908
948
  }
909
949
 
910
950
  /**
@@ -913,10 +953,7 @@ function CollectionFactory(params: {
913
953
  @method
914
954
  async limitMinting(): Promise<void> {
915
955
  await this.ensureCreatorSignature();
916
- const collectionData = CollectionData.unpack(
917
- this.packedData.getAndRequireEquals()
918
- );
919
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
956
+ const collectionData = await this.ensureNotPaused();
920
957
  collectionData.mintingIsLimited = Bool(true);
921
958
  this.packedData.set(collectionData.pack());
922
959
  this.emitEvent(
@@ -930,10 +967,7 @@ function CollectionFactory(params: {
930
967
  */
931
968
  @method
932
969
  async pause(): Promise<void> {
933
- const collectionData = CollectionData.unpack(
934
- this.packedData.getAndRequireEquals()
935
- );
936
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
970
+ const collectionData = await this.ensureNotPaused();
937
971
  const adminContract = this.getAdminContract();
938
972
  const canPause = await adminContract.canPause();
939
973
  canPause.assertTrue(CollectionErrors.noPermissionToPause);
@@ -966,6 +1000,7 @@ function CollectionFactory(params: {
966
1000
  */
967
1001
  @method
968
1002
  async pauseNFTBySignature(address: PublicKey): Promise<void> {
1003
+ await this.ensureNotPaused();
969
1004
  const tokenId = this.deriveTokenId();
970
1005
  const nft = new NFT(address, tokenId);
971
1006
  const owner = await nft.pause();
@@ -983,6 +1018,7 @@ function CollectionFactory(params: {
983
1018
  */
984
1019
  @method
985
1020
  async pauseNFTByProof(address: PublicKey): Promise<void> {
1021
+ await this.ensureNotPaused();
986
1022
  const tokenId = this.deriveTokenId();
987
1023
  const nft = new NFT(address, tokenId);
988
1024
  const owner = await nft.pause();
@@ -1002,6 +1038,7 @@ function CollectionFactory(params: {
1002
1038
  */
1003
1039
  @method
1004
1040
  async resumeNFT(address: PublicKey): Promise<void> {
1041
+ await this.ensureNotPaused();
1005
1042
  const tokenId = this.deriveTokenId();
1006
1043
  const nft = new NFT(address, tokenId);
1007
1044
  const owner = await nft.resume();
@@ -1019,6 +1056,7 @@ function CollectionFactory(params: {
1019
1056
  */
1020
1057
  @method
1021
1058
  async resumeNFTByProof(address: PublicKey): Promise<void> {
1059
+ await this.ensureNotPaused();
1022
1060
  const tokenId = this.deriveTokenId();
1023
1061
  const nft = new NFT(address, tokenId);
1024
1062
  const owner = await nft.resume();
@@ -1046,7 +1084,7 @@ function CollectionFactory(params: {
1046
1084
  const canChangeName = await adminContract.canChangeName(name);
1047
1085
  canChangeName.assertTrue(CollectionErrors.noPermissionToChangeName);
1048
1086
  this.collectionName.set(name);
1049
- this.emitEvent("setName", name);
1087
+ this.emitEvent("setName", new SetNameEvent({ name }));
1050
1088
  }
1051
1089
 
1052
1090
  /**
@@ -1064,7 +1102,7 @@ function CollectionFactory(params: {
1064
1102
  const canChangeBaseUri = await adminContract.canChangeBaseUri(baseURL);
1065
1103
  canChangeBaseUri.assertTrue(CollectionErrors.noPermissionToChangeBaseUri);
1066
1104
  this.baseURL.set(baseURL);
1067
- this.emitEvent("setBaseURL", baseURL);
1105
+ this.emitEvent("setBaseURL", new SetBaseURLEvent({ baseURL }));
1068
1106
  }
1069
1107
 
1070
1108
  /**
@@ -1082,7 +1120,7 @@ function CollectionFactory(params: {
1082
1120
  const canSetAdmin = await adminContract.canSetAdmin(admin);
1083
1121
  canSetAdmin.assertTrue(CollectionErrors.noPermissionToSetAdmin);
1084
1122
  this.admin.set(admin);
1085
- this.emitEvent("setAdmin", admin);
1123
+ this.emitEvent("setAdmin", new SetAdminEvent({ admin }));
1086
1124
  }
1087
1125
 
1088
1126
  /**
@@ -1095,10 +1133,7 @@ function CollectionFactory(params: {
1095
1133
  */
1096
1134
  @method
1097
1135
  async setRoyaltyFee(royaltyFee: UInt32): Promise<void> {
1098
- const collectionData = CollectionData.unpack(
1099
- this.packedData.getAndRequireEquals()
1100
- );
1101
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
1136
+ const collectionData = await this.ensureNotPaused();
1102
1137
  royaltyFee.assertLessThanOrEqual(
1103
1138
  UInt32.from(MAX_ROYALTY_FEE),
1104
1139
  CollectionErrors.invalidRoyaltyFee
@@ -1108,7 +1143,7 @@ function CollectionFactory(params: {
1108
1143
  canChangeRoyalty.assertTrue(CollectionErrors.noPermissionToChangeRoyalty);
1109
1144
  collectionData.royaltyFee = royaltyFee;
1110
1145
  this.packedData.set(collectionData.pack());
1111
- this.emitEvent("setRoyaltyFee", royaltyFee);
1146
+ this.emitEvent("setRoyaltyFee", new SetRoyaltyFeeEvent({ royaltyFee }));
1112
1147
  }
1113
1148
 
1114
1149
  /**
@@ -1121,10 +1156,7 @@ function CollectionFactory(params: {
1121
1156
  */
1122
1157
  @method
1123
1158
  async setTransferFee(transferFee: UInt64): Promise<void> {
1124
- const collectionData = CollectionData.unpack(
1125
- this.packedData.getAndRequireEquals()
1126
- );
1127
- collectionData.isPaused.assertFalse(CollectionErrors.collectionPaused);
1159
+ const collectionData = await this.ensureNotPaused();
1128
1160
  const adminContract = this.getAdminContract();
1129
1161
  const canChangeTransferFee = await adminContract.canChangeTransferFee(
1130
1162
  transferFee
@@ -1134,26 +1166,38 @@ function CollectionFactory(params: {
1134
1166
  );
1135
1167
  collectionData.transferFee = transferFee;
1136
1168
  this.packedData.set(collectionData.pack());
1137
- this.emitEvent("setTransferFee", transferFee);
1169
+ this.emitEvent(
1170
+ "setTransferFee",
1171
+ new SetTransferFeeEvent({ transferFee })
1172
+ );
1138
1173
  }
1139
1174
 
1140
1175
  /**
1141
- * Transfers ownership of the collection to a new owner.
1176
+ * Transfers ownership of the collection to a new creator.
1177
+ * This method is called transferOwnership as the Collection is implementing OwnableContract interface
1178
+ * For the Collection, the creator is the owner of the collection
1142
1179
  *
1143
- * @param to - The public key of the new owner.
1144
- * @returns The public key of the old owner.
1180
+ * @param to - The public key of the new creator.
1181
+ * @returns The public key of the old creator.
1145
1182
  */
1146
1183
  @method.returns(PublicKey)
1147
1184
  async transferOwnership(to: PublicKey): Promise<PublicKey> {
1148
1185
  await this.ensureCreatorSignature();
1149
- await this.ensureNotPaused();
1186
+ const collectionData = CollectionData.unpack(
1187
+ this.packedData.getAndRequireEquals()
1188
+ );
1189
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionNotPaused);
1190
+
1150
1191
  const adminContract = this.getAdminContract();
1151
1192
  const canChangeCreator = await adminContract.canChangeCreator(to);
1152
1193
  canChangeCreator.assertTrue(CollectionErrors.noPermissionToChangeCreator);
1153
1194
  const from = this.creator.getAndRequireEquals();
1154
- this.creator.set(to);
1195
+ // Pending creator public key can be empty, it cancels the transfer
1196
+ this.pendingCreatorX.set(to.x);
1197
+ collectionData.pendingCreatorIsOdd = to.isOdd;
1198
+ this.packedData.set(collectionData.pack());
1155
1199
  this.emitEvent(
1156
- "ownershipChange",
1200
+ "ownershipTransfer",
1157
1201
  new OwnershipChangeEvent({
1158
1202
  from,
1159
1203
  to,
@@ -1162,6 +1206,54 @@ function CollectionFactory(params: {
1162
1206
  return from;
1163
1207
  }
1164
1208
 
1209
+ /**
1210
+ * Transfers ownership of the collection to a new owner.
1211
+ *
1212
+ * @param to - The public key of the new owner.
1213
+ * @returns The public key of the old owner.
1214
+ */
1215
+ @method.returns(PublicKey)
1216
+ async acceptOwnership(): Promise<PublicKey> {
1217
+ const collectionData = CollectionData.unpack(
1218
+ this.packedData.getAndRequireEquals()
1219
+ );
1220
+ collectionData.isPaused.assertFalse(CollectionErrors.collectionNotPaused);
1221
+
1222
+ const pendingCreatorX = this.pendingCreatorX.getAndRequireEquals();
1223
+ const pendingCreator = PublicKey.from({
1224
+ x: pendingCreatorX,
1225
+ isOdd: collectionData.pendingCreatorIsOdd,
1226
+ });
1227
+ const emptyPublicKey = PublicKey.empty();
1228
+ pendingCreator
1229
+ .equals(emptyPublicKey)
1230
+ .assertFalse(CollectionErrors.pendingCreatorIsEmpty);
1231
+ // pendingCreator can be different from the sender, but it should sign the tx
1232
+ const pendingCreatorUpdate = AccountUpdate.createSigned(pendingCreator);
1233
+ pendingCreatorUpdate.body.useFullCommitment = Bool(true); // Prevent memo and fee change
1234
+
1235
+ // Check second time that the transfer is allowed
1236
+ const adminContract = this.getAdminContract();
1237
+ const canChangeCreator = await adminContract.canChangeCreator(
1238
+ pendingCreator
1239
+ );
1240
+ canChangeCreator.assertTrue(CollectionErrors.noPermissionToChangeCreator);
1241
+ const from = this.creator.getAndRequireEquals();
1242
+
1243
+ this.pendingCreatorX.set(emptyPublicKey.x);
1244
+ collectionData.pendingCreatorIsOdd = Bool(emptyPublicKey.isOdd);
1245
+ this.creator.set(pendingCreator);
1246
+ this.packedData.set(collectionData.pack());
1247
+ this.emitEvent(
1248
+ "ownershipAccepted",
1249
+ new OwnershipChangeEvent({
1250
+ from,
1251
+ to: pendingCreator,
1252
+ })
1253
+ );
1254
+ return from;
1255
+ }
1256
+
1165
1257
  @method.returns(NFTStateStruct)
1166
1258
  async getNFTState(address: PublicKey): Promise<NFTStateStruct> {
1167
1259
  const tokenId = this.deriveTokenId();
@@ -30,6 +30,7 @@ const NftErrors = {
30
30
  cannotChangeMetadataVerificationKeyHash:
31
31
  "Cannot change metadata verification key hash",
32
32
  cannotChangeOwner: "Cannot change owner",
33
+ cannotChangeApproval: "Cannot change approval",
33
34
  cannotChangeStorage: "Cannot change storage",
34
35
  cannotChangePauseState: "Cannot change pause state",
35
36
  noPermissionToPause: "No permission to pause",
@@ -212,6 +213,7 @@ class NFT extends SmartContract {
212
213
  data.owner = output.owner;
213
214
  data.approved = output.approved;
214
215
  data.version = output.version;
216
+ data.isPaused = output.isPaused;
215
217
 
216
218
  this.packedData.set(data.pack());
217
219
 
@@ -282,6 +284,7 @@ class NFT extends SmartContract {
282
284
  async approveAddress(approved: PublicKey): Promise<PublicKey> {
283
285
  const data = NFTData.unpack(this.packedData.getAndRequireEquals());
284
286
  data.isPaused.assertFalse(NftErrors.nftIsPaused);
287
+ data.canApprove.assertTrue(NftErrors.cannotChangeApproval);
285
288
  data.approved = approved;
286
289
  this.packedData.set(data.pack());
287
290
  this.emitEvent("approve", approved);