@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.
- package/README.md +12 -0
- package/dist/node/admin/advanced.d.ts +5 -1
- package/dist/node/contracts/admin.d.ts +100 -10
- package/dist/node/contracts/admin.js +130 -29
- package/dist/node/contracts/admin.js.map +1 -1
- package/dist/node/contracts/collection.d.ts +56 -35
- package/dist/node/contracts/collection.js +162 -71
- package/dist/node/contracts/collection.js.map +1 -1
- package/dist/node/contracts/nft.js +3 -0
- package/dist/node/contracts/nft.js.map +1 -1
- package/dist/node/contracts.d.ts +51 -33
- package/dist/node/index.cjs +447 -182
- package/dist/node/interfaces/collection.d.ts +5 -5
- package/dist/node/interfaces/events.d.ts +306 -16
- package/dist/node/interfaces/events.js +42 -4
- package/dist/node/interfaces/events.js.map +1 -1
- package/dist/node/interfaces/ownable.d.ts +7 -7
- package/dist/node/interfaces/ownable.js.map +1 -1
- package/dist/node/interfaces/pausable.d.ts +3 -3
- package/dist/node/interfaces/pausable.js.map +1 -1
- package/dist/node/interfaces/types.d.ts +182 -27
- package/dist/node/interfaces/types.js +120 -71
- package/dist/node/interfaces/types.js.map +1 -1
- package/dist/node/marketplace/auction.d.ts +7 -3
- package/dist/node/marketplace/auction.js +4 -4
- package/dist/node/marketplace/auction.js.map +1 -1
- package/dist/node/marketplace/bid.d.ts +5 -1
- package/dist/node/marketplace/bid.js +3 -5
- package/dist/node/marketplace/bid.js.map +1 -1
- package/dist/node/marketplace/nft-shares.d.ts +17 -5
- package/dist/node/marketplace/offer.d.ts +5 -1
- package/dist/node/util/div.js +10 -5
- package/dist/node/util/div.js.map +1 -1
- package/dist/node/vk.js +6 -6
- package/dist/node/vk.js.map +1 -1
- package/dist/node/zkprogram-example/game.d.ts +6 -17
- package/dist/node/zkprogram-example/update.d.ts +6 -17
- package/dist/tsconfig.node.tsbuildinfo +1 -1
- package/dist/tsconfig.web.tsbuildinfo +1 -1
- package/dist/web/admin/advanced.d.ts +5 -1
- package/dist/web/contracts/admin.d.ts +100 -10
- package/dist/web/contracts/admin.js +130 -29
- package/dist/web/contracts/admin.js.map +1 -1
- package/dist/web/contracts/collection.d.ts +56 -35
- package/dist/web/contracts/collection.js +162 -71
- package/dist/web/contracts/collection.js.map +1 -1
- package/dist/web/contracts/nft.js +3 -0
- package/dist/web/contracts/nft.js.map +1 -1
- package/dist/web/contracts.d.ts +51 -33
- package/dist/web/interfaces/collection.d.ts +5 -5
- package/dist/web/interfaces/events.d.ts +306 -16
- package/dist/web/interfaces/events.js +42 -4
- package/dist/web/interfaces/events.js.map +1 -1
- package/dist/web/interfaces/ownable.d.ts +7 -7
- package/dist/web/interfaces/ownable.js.map +1 -1
- package/dist/web/interfaces/pausable.d.ts +3 -3
- package/dist/web/interfaces/pausable.js.map +1 -1
- package/dist/web/interfaces/types.d.ts +182 -27
- package/dist/web/interfaces/types.js +120 -71
- package/dist/web/interfaces/types.js.map +1 -1
- package/dist/web/marketplace/auction.d.ts +7 -3
- package/dist/web/marketplace/auction.js +4 -4
- package/dist/web/marketplace/auction.js.map +1 -1
- package/dist/web/marketplace/bid.d.ts +5 -1
- package/dist/web/marketplace/bid.js +3 -5
- package/dist/web/marketplace/bid.js.map +1 -1
- package/dist/web/marketplace/nft-shares.d.ts +17 -5
- package/dist/web/marketplace/offer.d.ts +5 -1
- package/dist/web/util/div.js +10 -5
- package/dist/web/util/div.js.map +1 -1
- package/dist/web/vk.js +6 -6
- package/dist/web/vk.js.map +1 -1
- package/dist/web/zkprogram-example/game.d.ts +6 -17
- package/dist/web/zkprogram-example/update.d.ts +6 -17
- package/package.json +13 -13
- package/src/contracts/admin.ts +137 -24
- package/src/contracts/collection.ts +188 -96
- package/src/contracts/nft.ts +3 -0
- package/src/interfaces/collection.ts +11 -5
- package/src/interfaces/events.ts +47 -3
- package/src/interfaces/ownable.ts +1 -1
- package/src/interfaces/pausable.ts +1 -1
- package/src/interfaces/types.ts +152 -78
- package/src/marketplace/auction.ts +5 -4
- package/src/marketplace/bid.ts +4 -6
- package/src/util/div.ts +12 -5
- 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,
|
|
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:
|
|
74
|
+
update: NFTUpdateEvent,
|
|
72
75
|
transfer: TransferEvent,
|
|
73
76
|
approve: ApproveEvent,
|
|
74
77
|
upgradeNFTVerificationKey: UpgradeVerificationKeyEvent,
|
|
75
|
-
upgradeVerificationKey:
|
|
78
|
+
upgradeVerificationKey: UpgradeVerificationKeyEvent,
|
|
76
79
|
limitMinting: LimitMintingEvent,
|
|
77
80
|
pause: PauseEvent,
|
|
78
81
|
resume: PauseEvent,
|
|
79
82
|
pauseNFT: PauseNFTEvent,
|
|
80
83
|
resumeNFT: PauseNFTEvent,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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.
|
|
113
|
-
setTokenSymbol: Permissions.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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",
|
|
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
|
|
397
|
-
* @param
|
|
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 =
|
|
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 =
|
|
449
|
-
collectionData.
|
|
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
|
|
513
|
+
async adminApprovedTransferByProof(params) {
|
|
486
514
|
const { address, from, to, price, context } = params;
|
|
487
|
-
const collectionData =
|
|
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
|
|
554
|
+
async adminApprovedTransferBySignature(params) {
|
|
528
555
|
const { address, to, price, context } = params;
|
|
529
|
-
const collectionData =
|
|
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
|
-
*
|
|
556
|
-
*
|
|
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",
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
832
|
-
* @returns The public key of the old
|
|
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
|
-
|
|
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
|
-
|
|
842
|
-
this.
|
|
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", [
|
|
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", [
|
|
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", [
|
|
1021
|
+
__metadata("design:paramtypes", [TransferByProofParams]),
|
|
937
1022
|
__metadata("design:returntype", Promise)
|
|
938
|
-
], Collection.prototype, "
|
|
1023
|
+
], Collection.prototype, "adminApprovedTransferByProof", null);
|
|
939
1024
|
__decorate([
|
|
940
1025
|
method,
|
|
941
1026
|
__metadata("design:type", Function),
|
|
942
|
-
__metadata("design:paramtypes", [
|
|
1027
|
+
__metadata("design:paramtypes", [TransferBySignatureParams]),
|
|
943
1028
|
__metadata("design:returntype", Promise)
|
|
944
|
-
], Collection.prototype, "
|
|
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),
|