Smart Contract
TheFabricantS2ItemNFT
A.7752ea736384322f.TheFabricantS2ItemNFT
1/*
2 Description: TheFabricantS2ItemNFT Contract
3
4 TheFabricantS2ItemNFT NFTs are mintable by anyone, but requires a combination of 1
5 TheFabricantS2GarmentNFT NFT and 1 TheFabricantS2MaterialNFT NFT.
6*/
7
8import NonFungibleToken from 0x1d7e57aa55817448
9
10import FungibleToken from 0xf233dcee88fe0abe
11
12import TheFabricantS2GarmentNFT from 0x7752ea736384322f
13
14import TheFabricantS2MaterialNFT from 0x7752ea736384322f
15
16access(all)
17contract TheFabricantS2ItemNFT: NonFungibleToken{
18
19 // -----------------------------------------------------------------------
20 // TheFabricantS2ItemNFT contract Events
21 // -----------------------------------------------------------------------
22
23 // Emitted when the Item contract is created
24 access(all)
25 event ContractInitialized()
26
27 // Emitted when a new ItemData struct is created
28 access(all)
29 event ItemDataCreated(itemDataID: UInt32, coCreator: Address, metadatas:{ String: TheFabricantS2ItemNFT.Metadata})
30
31 // Emitted when a mutable metadata in an itemData is changed
32 access(all)
33 event ItemMetadataChanged(itemDataID: UInt32, metadataKey: String, metadataValue: String)
34
35 // Emitted when a mutable metadata in an itemData is set to immutable
36 access(all)
37 event ItemMetadataImmutable(itemDataID: UInt32, metadataKey: String)
38
39 // Emitted when an Item is minted
40 access(all)
41 event ItemMinted(itemID: UInt64, itemDataID: UInt32, serialNumber: UInt32)
42
43 // Emitted when an Item's name is changed
44 access(all)
45 event ItemNameChanged(id: UInt64, name: String)
46
47 // Emitted when an ItemData is allocated
48 access(all)
49 event ItemDataAllocated(garmentDataID: UInt32, materialDataID: UInt32, primaryColor: String, secondaryColor: String, key: String, itemDataID: UInt32)
50
51 // Emitted when the items are set to be splittable
52 access(all)
53 event ItemNFTNowSplittable()
54
55 // Emitted when the number of items with ItemDataID that can be minted changes
56 access(all)
57 event numberItemDataMintableChanged(itemDataID: UInt32, number: UInt32)
58
59 // Emitted when an ItemData is retired
60 access(all)
61 event ItemDataIDRetired(itemDataID: UInt32)
62
63 // Emitted when a color is added to availablePrimaryColors array
64 access(all)
65 event PrimaryColorAdded(color: String)
66
67 // Emitted when a color is added to availableSecondaryColors array
68 access(all)
69 event SecondaryColorAdded(color: String)
70
71 // Emitted when a color is removed from availablePrimaryColors array
72 access(all)
73 event PrimaryColorRemoved(color: String)
74
75 // Emitted when a color is removed from availableSecondaryColors array
76 access(all)
77 event SecondaryColorRemoved(color: String)
78
79 // Events for Collection-related actions
80 //
81 // Emitted when a Item is withdrawn from a Collection
82 access(all)
83 event Withdraw(id: UInt64, from: Address?)
84
85 // Emitted when a Item is deposited into a Collection
86 access(all)
87 event Deposit(id: UInt64, to: Address?)
88
89 // Emitted when a Item is destroyed
90 access(all)
91 event ItemDestroyed(id: UInt64)
92
93 // -----------------------------------------------------------------------
94 // contract-level fields.
95 // These contain actual values that are stored in the smart contract.
96 // -----------------------------------------------------------------------
97 access(all)
98 let CollectionStoragePath: StoragePath
99
100 access(all)
101 let CollectionPublicPath: PublicPath
102
103 access(all)
104 let AdminStoragePath: StoragePath
105
106 // itemDataID: number of NFTs with that ItemDataID are minted
107 access(self)
108 var numberMintedPerItem:{ UInt32: UInt32}
109
110 // itemDataID: how many items with itemData can be minted
111 access(self)
112 var numberMintablePerItemData:{ UInt32: UInt32}
113
114 // itemDataID: itemData struct
115 access(self)
116 var itemDatas:{ UInt32: ItemData}
117
118 // concat of garmentDataID_materialDataID_primaryColor_secondaryColor: itemDataID (eg. 1_1_FFFFFF_000000)
119 access(self)
120 var itemDataAllocation:{ String: UInt32}
121
122 // itemDataID: whether item with ItemDataID cannot be minted anymore
123 access(self)
124 var isItemDataRetired:{ UInt32: Bool}
125
126 // Dictionary of the nft with id and its current owner address
127 access(self)
128 var nftIDToOwner:{ UInt64: Address}
129
130 // array of available primaryColors
131 access(self)
132 var availablePrimaryColors: [String]
133
134 // array of available secondaryColors
135 access(self)
136 var availableSecondaryColors: [String]
137
138 // Keeps track of how many unique ItemDatas are created
139 access(all)
140 var nextItemDataID: UInt32
141
142 // Keeps track of how many unique ItemDataAllocations are created
143 access(all)
144 var nextItemDataAllocation: UInt32
145
146 // Are garment and material removable from item
147 access(all)
148 var isSplittable: Bool
149
150 access(all)
151 var totalSupply: UInt64
152
153 // metadata of an ItemData struct that contains the metadata value
154 // and whether it is mutable
155 access(all)
156 struct Metadata{
157 access(all)
158 var metadataValue: String
159
160 access(all)
161 var mutable: Bool
162
163 init(metadataValue: String, mutable: Bool){
164 self.metadataValue = metadataValue
165 self.mutable = mutable
166 }
167
168 access(all)
169 fun setMetadataImmutable(){
170 pre{
171 self.mutable == true:
172 "metadata is already immutable"
173 }
174 self.mutable = false
175 }
176
177 access(all)
178 fun setMetadataValue(metadataValue: String){
179 pre{
180 self.mutable == true:
181 "metadata is already immutable"
182 }
183 self.metadataValue = metadataValue
184 }
185 }
186
187 access(all)
188 struct ItemData{
189
190 // The unique ID for the Item Data
191 access(all)
192 let itemDataID: UInt32
193
194 // stores link to image
195 // the address of the user who created the unique combination
196 access(all)
197 let coCreator: Address
198
199 // other metadata
200 access(self)
201 let metadatas:{ String: TheFabricantS2ItemNFT.Metadata}
202
203 init(coCreator: Address, metadatas:{ String: TheFabricantS2ItemNFT.Metadata}){
204 self.itemDataID = TheFabricantS2ItemNFT.nextItemDataID
205 self.coCreator = coCreator
206 self.metadatas = metadatas
207 TheFabricantS2ItemNFT.isItemDataRetired[self.itemDataID] = false
208
209 // set default number mintable of itemData to 1
210 TheFabricantS2ItemNFT.numberMintablePerItemData[self.itemDataID] = 1
211
212 // Increment the ID so that it isn't used again
213 TheFabricantS2ItemNFT.nextItemDataID = TheFabricantS2ItemNFT.nextItemDataID + 1 as UInt32
214 emit ItemDataCreated(itemDataID: self.itemDataID, coCreator: self.coCreator, metadatas: self.metadatas)
215 }
216
217 // change a mutable metadata in the metadata struct
218 access(all)
219 fun setMetadata(metadataKey: String, metadataValue: String){
220 (self.metadatas[metadataKey]!).setMetadataValue(metadataValue: metadataValue)
221 emit ItemMetadataChanged(itemDataID: self.itemDataID, metadataKey: metadataKey, metadataValue: metadataValue)
222 }
223
224 // prevent changing of a metadata in the metadata struct
225 access(all)
226 fun setMetadataKeyImmutable(metadataKey: String){
227 (self.metadatas[metadataKey]!).setMetadataImmutable()
228 emit ItemMetadataImmutable(itemDataID: self.itemDataID, metadataKey: metadataKey)
229 }
230
231 access(all)
232 fun getMetadata():{ String: TheFabricantS2ItemNFT.Metadata}{
233 return self.metadatas
234 }
235 }
236
237 access(all)
238 struct Item{
239
240 // The ID of the itemData that the item references
241 access(all)
242 let itemDataID: UInt32
243
244 // The N'th NFT with 'ItemDataID' minted
245 access(all)
246 let serialNumber: UInt32
247
248 init(itemDataID: UInt32){
249 pre{
250 Int(TheFabricantS2ItemNFT.numberMintedPerItem[itemDataID]!) < Int(TheFabricantS2ItemNFT.numberMintablePerItemData[itemDataID]!):
251 "maximum number of TheFabricantS2ItemNFT with itemDataID reached"
252 }
253 self.itemDataID = itemDataID
254
255 // Increment the ID so that it isn't used again
256 TheFabricantS2ItemNFT.numberMintedPerItem[itemDataID] = TheFabricantS2ItemNFT.numberMintedPerItem[itemDataID]! + 1 as UInt32
257 self.serialNumber = TheFabricantS2ItemNFT.numberMintedPerItem[itemDataID]!
258 }
259 }
260
261 // Royalty struct that each TheFabricantS2ItemNFT will contain
262 access(all)
263 struct Royalty{
264 access(all)
265 let wallet: Capability<&{FungibleToken.Receiver}>
266
267 access(all)
268 let initialCut: UFix64
269
270 access(all)
271 let cut: UFix64
272
273 /// @param wallet : The wallet to send royalty too
274 init(wallet: Capability<&{FungibleToken.Receiver}>, initialCut: UFix64, cut: UFix64){
275 self.wallet = wallet
276 self.initialCut = initialCut
277 self.cut = cut
278 }
279 }
280
281 // The resource that represents the Item NFTs
282 //
283 access(all)
284 resource NFT: NonFungibleToken.NFT{
285
286 // Global unique Item ID
287 access(all)
288 let id: UInt64
289
290 // struct of Item
291 access(all)
292 let item: Item
293
294 // name of nft, can be changed
295 access(all)
296 var name: String
297
298 // Royalty struct
299 access(all)
300 let royaltyVault: TheFabricantS2ItemNFT.Royalty
301
302 // after you remove the garment and material from the item, the TheFabricantS2ItemNFT will be considered "dead".
303 // accounts will be unable to deposit, withdraw or call functions of the nft.
304 access(all)
305 var isDead: Bool
306
307 // this is where the garment nft is stored, it cannot be moved out
308 access(self)
309 var garment: @TheFabricantS2GarmentNFT.NFT?
310
311 // this is where the material nft is stored, it cannot be moved out
312 access(self)
313 var material: @TheFabricantS2MaterialNFT.NFT?
314
315 init(serialNumber: UInt32, name: String, itemDataID: UInt32, royaltyVault: TheFabricantS2ItemNFT.Royalty, garment: @TheFabricantS2GarmentNFT.NFT, material: @TheFabricantS2MaterialNFT.NFT){
316 TheFabricantS2ItemNFT.totalSupply = TheFabricantS2ItemNFT.totalSupply + 1 as UInt64
317 self.id = TheFabricantS2ItemNFT.totalSupply
318 self.name = name
319 self.royaltyVault = royaltyVault
320 self.isDead = false
321 self.garment <- garment
322 self.material <- material
323 self.item = Item(itemDataID: itemDataID)
324
325 // Emitted when a Item is minted
326 emit ItemMinted(itemID: self.id, itemDataID: itemDataID, serialNumber: serialNumber)
327 }
328
329 //Make Item considered dead. Deposit garment and material to respective vaults
330 access(contract)
331 fun split(garmentCap: Capability<&{TheFabricantS2GarmentNFT.GarmentCollectionPublic}>, materialCap: Capability<&{TheFabricantS2MaterialNFT.MaterialCollectionPublic}>){
332 pre{
333 !self.isDead:
334 "Cannot split. Item is dead"
335 TheFabricantS2ItemNFT.isSplittable:
336 "Item is set to unsplittable"
337 garmentCap.check():
338 "Garment Capability is invalid"
339 materialCap.check():
340 "Material Capability is invalid"
341 }
342 let garmentOptional <- self.garment <- nil
343 let materialOptional <- self.material <- nil
344 let garmentRecipient = garmentCap.borrow()!
345 let materialRecipient = materialCap.borrow()!
346 let garment <- garmentOptional!
347 let material <- materialOptional!
348 let garmentNFT <- garment as! @{NonFungibleToken.NFT}
349 let materialNFT <- material as! @{NonFungibleToken.NFT}
350 garmentRecipient.deposit(token: <-garmentNFT)
351 materialRecipient.deposit(token: <-materialNFT)
352 TheFabricantS2ItemNFT.numberMintedPerItem[self.item.itemDataID] = TheFabricantS2ItemNFT.numberMintedPerItem[self.item.itemDataID]! - 1 as UInt32
353 self.isDead = true
354 }
355
356 // get a reference to the garment that item stores
357 access(all)
358 fun borrowGarment(): &TheFabricantS2GarmentNFT.NFT? {
359 return &self.garment as &TheFabricantS2GarmentNFT.NFT?
360 }
361
362 // get a reference to the material that item stores
363 access(all)
364 fun borrowMaterial(): &TheFabricantS2MaterialNFT.NFT?{
365 return &self.material as &TheFabricantS2MaterialNFT.NFT?
366 }
367
368 access(all)
369 fun createEmptyCollection(): @{NonFungibleToken.Collection}{
370 return <-create Collection()
371 }
372
373 access(all)
374 view fun getViews(): [Type]{
375 return []
376 }
377
378 access(all)
379 fun resolveView(_ view: Type): AnyStruct?{
380 return nil
381 }
382 }
383
384 //destroy item if it is considered dead
385 access(all)
386 fun cleanDeadItems(item: @TheFabricantS2ItemNFT.NFT){
387 pre{
388 item.isDead:
389 "Cannot destroy, item not dead"
390 }
391 destroy item
392 }
393
394 // mint the NFT, based on a combination of garment, material, primaryColor and secondaryColor.
395 // The itemData that is used to mint the Item is based on the garment and material' garmentDataID and materialDataID
396 access(all)
397 fun mintNFT(name: String, royaltyVault: TheFabricantS2ItemNFT.Royalty, garment: @TheFabricantS2GarmentNFT.NFT, material: @TheFabricantS2MaterialNFT.NFT, primaryColor: String, secondaryColor: String): @NFT{
398 let garmentDataID = garment.garment.garmentDataID
399 let materialDataID = material.material.materialDataID
400 let key = TheFabricantS2ItemNFT.getUniqueString(garmentDataID: garmentDataID, materialDataID: materialDataID, primaryColor: primaryColor, secondaryColor: secondaryColor)
401
402 //check whether unique combination is allocated
403 let itemDataID = TheFabricantS2ItemNFT.itemDataAllocation[key] ?? panic("Cannot mint Item. ItemData not found")
404
405 //check whether itemdata is retired
406 if TheFabricantS2ItemNFT.isItemDataRetired[itemDataID]!{
407 panic("Cannot mint Item. ItemDataID retired")
408 }
409 let numInItem = TheFabricantS2ItemNFT.numberMintedPerItem[itemDataID] ?? panic("Maximum number of Items with itemDataID minted")
410 let item <- create NFT(serialNumber: numInItem + 1, name: name, itemDataID: itemDataID, royaltyVault: royaltyVault, garment: <-garment, material: <-material)
411 return <-item
412 }
413
414 // This is the interface that users can cast their Item Collection as
415 // to allow others to deposit Items into their Collection. It also allows for reading
416 // the IDs of Items in the Collection.
417 access(all)
418 resource interface ItemCollectionPublic{
419 access(all)
420 fun deposit(token: @{NonFungibleToken.NFT})
421
422 access(all)
423 fun batchDeposit(tokens: @{NonFungibleToken.Collection})
424
425 access(all)
426 fun getIDs(): [UInt64]
427
428 access(all)
429 view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
430
431 access(all)
432 fun borrowItem(id: UInt64): &TheFabricantS2ItemNFT.NFT?{
433 // If the result isn't nil, the id of the returned reference
434 // should be the same as the argument to the function
435 post{
436 result == nil || result?.id == id:
437 "Cannot borrow Item reference: The ID of the returned reference is incorrect"
438 }
439 }
440 }
441
442 // Collection is a resource that every user who owns NFTs
443 // will store in their account to manage their NFTS
444 //
445 access(all)
446 resource Collection: ItemCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic{
447 // Dictionary of Item conforming tokens
448 // NFT is a resource type with a UInt64 ID field
449 access(all)
450 var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
451
452 init(){
453 self.ownedNFTs <-{}
454 }
455
456 access(all)
457 fun split(id: UInt64, garmentCap: Capability<&{TheFabricantS2GarmentNFT.GarmentCollectionPublic}>, materialCap: Capability<&{TheFabricantS2MaterialNFT.MaterialCollectionPublic}>){
458 let token <- self.ownedNFTs.remove(key: id) ?? panic("Cannot withdraw: Item does not exist in the collection")
459 let item <- token as! @TheFabricantS2ItemNFT.NFT
460 item.split(garmentCap: garmentCap, materialCap: materialCap)
461 self.ownedNFTs[id] <-! item
462 }
463
464 // withdraw removes an Item from the Collection and moves it to the caller
465 //
466 // Parameters: withdrawID: The ID of the NFT
467 // that is to be removed from the Collection
468 //
469 // returns: @NonFungibleToken.NFT the token that was withdrawn
470 access(NonFungibleToken.Withdraw)
471 fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{
472 // Remove the nft from the Collection
473 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Cannot withdraw: Item does not exist in the collection")
474 emit Withdraw(id: token.id, from: self.owner?.address)
475
476 // Return the withdrawn token
477 return <-token
478 }
479
480 // batchWithdraw withdraws multiple tokens and returns them as a Collection
481 //
482 // Parameters: ids: An array of IDs to withdraw
483 //
484 // Returns: @NonFungibleToken.Collection: A collection that contains
485 // the withdrawn Items
486 //
487 access(all)
488 fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection}{
489 // Create a new empty Collection
490 var batchCollection <- create Collection()
491
492 // Iterate through the ids and withdraw them from the Collection
493 for id in ids{
494 batchCollection.deposit(token: <-self.withdraw(withdrawID: id))
495 }
496
497 // Return the withdrawn tokens
498 return <-batchCollection
499 }
500
501 // deposit takes a Item and adds it to the Collections dictionary
502 //
503 // Paramters: token: the NFT to be deposited in the collection
504 //
505 access(all)
506 fun deposit(token: @{NonFungibleToken.NFT}){
507 //todo: someFunction that transfers royalty
508 // Cast the deposited token as NFT to make sure
509 // it is the correct type
510 let token <- token as! @TheFabricantS2ItemNFT.NFT
511
512 // Get the token's ID
513 let id = token.id
514
515 // Add the new token to the dictionary
516 let oldToken <- self.ownedNFTs[id] <- token
517
518 // Set the global mapping of nft id to new owner
519 TheFabricantS2ItemNFT.nftIDToOwner[id] = self.owner?.address
520
521 // Only emit a deposit event if the Collection
522 // is in an account's storage
523 if self.owner?.address != nil{
524 emit Deposit(id: id, to: self.owner?.address)
525 }
526
527 // Destroy the empty old token that was "removed"
528 destroy oldToken
529 }
530
531 // batchDeposit takes a Collection object as an argument
532 // and deposits each contained NFT into this Collection
533 access(all)
534 fun batchDeposit(tokens: @{NonFungibleToken.Collection}){
535 // Get an array of the IDs to be deposited
536 let keys = tokens.getIDs()
537
538 // Iterate through the keys in the collection and deposit each one
539 for key in keys{
540 self.deposit(token: <-tokens.withdraw(withdrawID: key))
541 }
542
543 // Destroy the empty Collection
544 destroy tokens
545 }
546
547 // getIDs returns an array of the IDs that are in the Collection
548 access(all)
549 view fun getIDs(): [UInt64]{
550 return self.ownedNFTs.keys
551 }
552
553 // borrowNFT Returns a borrowed reference to a Item in the Collection
554 // so that the caller can read its ID
555 //
556 // Parameters: id: The ID of the NFT to get the reference for
557 //
558 // Returns: A reference to the NFT
559 //
560 // Note: This only allows the caller to read the ID of the NFT,
561 // not an specific data. Please use borrowItem to
562 // read Item data.
563 //
564 access(all)
565 view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{
566 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
567 }
568
569 // Parameters: id: The ID of the NFT to get the reference for
570 //
571 // Returns: A reference to the NFT
572 access(all)
573 fun borrowItem(id: UInt64): &TheFabricantS2ItemNFT.NFT?{
574 if self.ownedNFTs[id] != nil{
575 let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
576 return ref as! &TheFabricantS2ItemNFT.NFT
577 } else{
578 return nil
579 }
580 }
581
582 access(all)
583 view fun getSupportedNFTTypes():{ Type: Bool}{
584 panic("implement me")
585 }
586
587 access(all)
588 view fun isSupportedNFTType(type: Type): Bool{
589 panic("implement me")
590 }
591
592 access(all)
593 fun createEmptyCollection(): @{NonFungibleToken.Collection}{
594 return <-create Collection()
595 }
596
597 // If a transaction destroys the Collection object,
598 // All the NFTs contained within are also destroyed!
599 //
600 }
601
602 // Admin is a special authorization resource that
603 // allows the owner to perform important functions to modify the
604 // various aspects of the Items and NFTs
605 //
606 access(all)
607 resource Admin{
608
609 // create itemdataid allocation based on the
610 // garmentDataID, materialDataID, primaryColor and secondaryColor
611 // and create an itemdata struct based on it
612 access(all)
613 fun createItemDataWithAllocation(garmentDataID: UInt32, materialDataID: UInt32, primaryColor: String, secondaryColor: String, metadatas:{ String: TheFabricantS2ItemNFT.Metadata}, coCreator: Address): UInt32{
614
615 // check whether colors are valid colors
616 let pCAvailable = TheFabricantS2ItemNFT.availablePrimaryColors.contains(primaryColor)
617 let pSCAvailable = TheFabricantS2ItemNFT.availableSecondaryColors.contains(secondaryColor)
618 let garmentDataPresent = TheFabricantS2GarmentNFT.getGarmentDatas().containsKey(garmentDataID)
619 let materialDataPresent = TheFabricantS2MaterialNFT.getMaterialDatas().containsKey(materialDataID)
620 assert(pCAvailable, message: "PrimaryColor not available")
621 assert(pSCAvailable, message: "SecondaryColor not available")
622 assert(garmentDataPresent, message: "GarmentData not found")
623 assert(materialDataPresent, message: "MaterialData not found")
624
625 // set the primaryColor and secondaryColor metadata of Metadata struct
626 metadatas["primaryColor"] = TheFabricantS2ItemNFT.Metadata(metadataValue: primaryColor, mutable: false)
627 metadatas["secondaryColor"] = TheFabricantS2ItemNFT.Metadata(metadataValue: secondaryColor, mutable: false)
628
629 // check whether unique combination is allocated already
630 let key = TheFabricantS2ItemNFT.getUniqueString(garmentDataID: garmentDataID, materialDataID: materialDataID, primaryColor: primaryColor, secondaryColor: secondaryColor)
631 if TheFabricantS2ItemNFT.itemDataAllocation.containsKey(key){
632 panic("Item already allocated")
633 }
634
635 // set unique combination's itemdataallocation, then increment nextItemDataAllocation
636 TheFabricantS2ItemNFT.itemDataAllocation[key] = TheFabricantS2ItemNFT.nextItemDataAllocation
637 emit ItemDataAllocated(garmentDataID: garmentDataID, materialDataID: materialDataID, primaryColor: primaryColor, secondaryColor: secondaryColor, key: key, itemDataID: TheFabricantS2ItemNFT.nextItemDataAllocation)
638 TheFabricantS2ItemNFT.nextItemDataAllocation = TheFabricantS2ItemNFT.nextItemDataAllocation + 1 as UInt32
639
640 // create new itemData
641 var newItem = ItemData(coCreator: coCreator, metadatas: metadatas)
642
643 // Store it in the contract storage
644 let newID = newItem.itemDataID
645 TheFabricantS2ItemNFT.itemDatas[newID] = newItem
646 TheFabricantS2ItemNFT.numberMintedPerItem[newID] = 0 as UInt32
647 return newID
648 }
649
650 // change the metadatavalue of an itemData
651 access(all)
652 fun setMetadata(itemDataID: UInt32, metadataKey: String, metadataValue: String){
653 let itemDataTemp = TheFabricantS2ItemNFT.itemDatas[itemDataID]!
654 itemDataTemp.setMetadata(metadataKey: metadataKey, metadataValue: metadataValue)
655 TheFabricantS2ItemNFT.itemDatas[itemDataID] = itemDataTemp
656 }
657
658 // make the metadatavalue of an itemData immutable
659 access(all)
660 fun setMetadataImmutable(itemData: UInt32, metadataKey: String){
661 let itemDataTemp = TheFabricantS2ItemNFT.itemDatas[itemData]!
662 itemDataTemp.setMetadataKeyImmutable(metadataKey: metadataKey)
663 TheFabricantS2ItemNFT.itemDatas[itemData] = itemDataTemp
664 }
665
666 // add the primaryColor to array of availablePrimaryColors
667 access(all)
668 fun addPrimaryColor(color: String){
669 pre{
670 !TheFabricantS2ItemNFT.availablePrimaryColors.contains(color):
671 "availablePrimaryColors already has color"
672 }
673 TheFabricantS2ItemNFT.availablePrimaryColors.append(color)
674 emit PrimaryColorAdded(color: color)
675 }
676
677 // add the primaryColor to array of availablePrimaryColors
678 access(all)
679 fun addSecondaryColor(color: String){
680 pre{
681 !TheFabricantS2ItemNFT.availableSecondaryColors.contains(color):
682 "availableSecondaryColors does not contain color"
683 }
684 TheFabricantS2ItemNFT.availableSecondaryColors.append(color)
685 emit SecondaryColorAdded(color: color)
686 }
687
688 // add the primaryColor to array of availablePrimaryColors
689 access(all)
690 fun removePrimaryColor(color: String){
691 pre{
692 TheFabricantS2ItemNFT.availablePrimaryColors.contains(color):
693 "availablePrimaryColors does not contain color"
694 }
695 var index = 0
696 for primaryColor in TheFabricantS2ItemNFT.availablePrimaryColors{
697 if primaryColor == color{
698 TheFabricantS2ItemNFT.availablePrimaryColors.remove(at: index)
699 } else{
700 index = index + 1
701 }
702 }
703 emit PrimaryColorRemoved(color: color)
704 }
705
706 // add the primaryColor to array of availablePrimaryColors
707 access(all)
708 fun removeSecondaryColor(color: String){
709 pre{
710 TheFabricantS2ItemNFT.availableSecondaryColors.contains(color):
711 "availableSecondaryColors already has color"
712 }
713 var index = 0
714 for secondaryColor in TheFabricantS2ItemNFT.availableSecondaryColors{
715 if secondaryColor == color{
716 TheFabricantS2ItemNFT.availableSecondaryColors.remove(at: index)
717 } else{
718 index = index + 1
719 }
720 }
721 emit SecondaryColorRemoved(color: color)
722 }
723
724 // createNewAdmin creates a new Admin resource
725 //
726 access(all)
727 fun createNewAdmin(): @Admin{
728 return <-create Admin()
729 }
730
731 // Change the royalty percentage of the contract
732 access(all)
733 fun makeSplittable(){
734 TheFabricantS2ItemNFT.isSplittable = true
735 emit ItemNFTNowSplittable()
736 }
737
738 // Change the number of itemdata mintable
739 access(all)
740 fun changeItemDataNumberMintable(itemDataID: UInt32, number: UInt32){
741 TheFabricantS2ItemNFT.numberMintablePerItemData[itemDataID] = number
742 emit numberItemDataMintableChanged(itemDataID: itemDataID, number: number)
743 }
744
745 // Retire itemData so that it cannot be used to mint anymore
746 access(all)
747 fun retireItemData(itemDataID: UInt32){
748 pre{
749 TheFabricantS2ItemNFT.isItemDataRetired[itemDataID] != nil:
750 "Cannot retire item: Item doesn't exist!"
751 }
752 if !TheFabricantS2ItemNFT.isItemDataRetired[itemDataID]!{
753 TheFabricantS2ItemNFT.isItemDataRetired[itemDataID] = true
754 emit ItemDataIDRetired(itemDataID: itemDataID)
755 }
756 }
757 }
758
759 // -----------------------------------------------------------------------
760 // Item contract-level function definitions
761 // -----------------------------------------------------------------------
762 // createEmptyCollection creates a new, empty Collection object so that
763 // a user can store it in their account storage.
764 // Once they have a Collection in their storage, they are able to receive
765 // Items in transactions.
766 //
767 access(all)
768 fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{
769 return <-create TheFabricantS2ItemNFT.Collection()
770 }
771
772 // get dictionary of numberMintedPerItem
773 access(all)
774 fun getNumberMintedPerItem():{ UInt32: UInt32}{
775 return TheFabricantS2ItemNFT.numberMintedPerItem
776 }
777
778 // get how many Items with itemDataID are minted
779 access(all)
780 fun getItemNumberMinted(id: UInt32): UInt32{
781 let numberMinted = TheFabricantS2ItemNFT.numberMintedPerItem[id] ?? panic("itemDataID not found")
782 return numberMinted
783 }
784
785 // get the ItemData of a specific id
786 access(all)
787 fun getItemData(id: UInt32): ItemData{
788 let itemData = TheFabricantS2ItemNFT.itemDatas[id] ?? panic("itemDataID not found")
789 return itemData
790 }
791
792 // get the map of item data allocations
793 access(all)
794 fun getItemDataAllocations():{ String: UInt32}{
795 let itemDataAllocation = TheFabricantS2ItemNFT.itemDataAllocation
796 return itemDataAllocation
797 }
798
799 // get the itemData allocation from the garment and material dataID
800 access(all)
801 fun getItemDataAllocation(garmentDataID: UInt32, materialDataID: UInt32, primaryColor: String, secondaryColor: String): UInt32{
802 let itemDataAllocation = TheFabricantS2ItemNFT.itemDataAllocation[TheFabricantS2ItemNFT.getUniqueString(garmentDataID: garmentDataID, materialDataID: materialDataID, primaryColor: primaryColor, secondaryColor: secondaryColor)] ?? panic("garment and material dataID pair not allocated")
803 return itemDataAllocation
804 }
805
806 // get all ItemDatas created
807 access(all)
808 fun getItemDatas():{ UInt32: ItemData}{
809 return TheFabricantS2ItemNFT.itemDatas
810 }
811
812 access(all)
813 fun getAvailablePrimaryColors(): [String]{
814 return TheFabricantS2ItemNFT.availablePrimaryColors
815 }
816
817 access(all)
818 fun getAvailableSecondaryColors(): [String]{
819 return TheFabricantS2ItemNFT.availableSecondaryColors
820 }
821
822 // get dictionary of itemdataids and whether they are retired
823 access(all)
824 fun getItemDatasRetired():{ UInt32: Bool}{
825 return TheFabricantS2ItemNFT.isItemDataRetired
826 }
827
828 // get bool of if itemdataid is retired
829 access(all)
830 fun getItemDataRetired(itemDataID: UInt32): Bool?{
831 return TheFabricantS2ItemNFT.isItemDataRetired[itemDataID]!
832 }
833
834 access(all)
835 fun getUniqueString(garmentDataID: UInt32, materialDataID: UInt32, primaryColor: String, secondaryColor: String): String{
836 return garmentDataID.toString().concat("_").concat(materialDataID.toString()).concat("_").concat(primaryColor).concat("_").concat(secondaryColor)
837 }
838
839 access(all)
840 fun getNumberMintablePerItemData():{ UInt32: UInt32}{
841 return TheFabricantS2ItemNFT.numberMintablePerItemData
842 }
843
844 access(all)
845 fun getNftIdToOwner():{ UInt64: Address}{
846 return TheFabricantS2ItemNFT.nftIDToOwner
847 }
848
849 access(all)
850 view fun getContractViews(resourceType: Type?): [Type]{
851 return []
852 }
853
854 access(all)
855 view fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct?{
856 return nil
857 }
858
859 // -----------------------------------------------------------------------
860 // initialization function
861 // -----------------------------------------------------------------------
862 //
863 init(){
864 self.itemDatas ={}
865 self.itemDataAllocation ={}
866 self.numberMintedPerItem ={}
867 self.numberMintablePerItemData ={}
868 self.nextItemDataID = 1
869 self.nextItemDataAllocation = 1
870 self.isSplittable = false
871 self.isItemDataRetired ={}
872 self.nftIDToOwner ={}
873 self.totalSupply = 0
874 self.availablePrimaryColors = []
875 self.availableSecondaryColors = []
876 self.CollectionPublicPath = /public/S2ItemCollection0028
877 self.CollectionStoragePath = /storage/S2ItemCollection0028
878 self.AdminStoragePath = /storage/S2ItemAdmin0028
879
880 // Put a new Collection in storage
881 self.account.storage.save<@Collection>(<-create Collection(), to: self.CollectionStoragePath)
882
883 // Create a public capability for the Collection
884 var capability_1 = self.account.capabilities.storage.issue<&{ItemCollectionPublic}>(self.CollectionStoragePath)
885 self.account.capabilities.publish(capability_1, at: self.CollectionPublicPath)
886
887 // Put the Minter in storage
888 self.account.storage.save<@Admin>(<-create Admin(), to: self.AdminStoragePath)
889 emit ContractInitialized()
890 }
891}
892