Smart Contract
YoungBoysBern
A.20187093790b9aef.YoungBoysBern
1/*
2 Description:
3
4 authors: Joseph Djenandji, Matthew Balazsi, Jennifer McIntyre
5
6*/
7
8// import NonFungibleToken from "./NonFungibleToken.cdc"
9// import FungibleToken from "./FungibleToken.cdc"
10// import MetadataViews from "./MetadataViews.cdc"
11
12// for emulator
13// import NonFungibleToken from "0xNonFungibleToken"
14// import ViewResolver from "0xViewResolver"
15// import MetadataViews from "0xMetadataViews"
16
17// for tests
18// import NonFungibleToken from NonFungibleToken
19// import MetadataViews from MetadataViews
20
21// for testnet
22// import NonFungibleToken from 0x631e88ae7f1d7c20
23// import MetadataViews from 0x631e88ae7f1d7c20
24// import ViewResolver from 0x631e88ae7f1d7c20
25
26// for mainnet
27import NonFungibleToken from 0x1d7e57aa55817448
28import MetadataViews from 0x1d7e57aa55817448
29import ViewResolver from 0x1d7e57aa55817448
30
31
32access(all) contract YoungBoysBern: NonFungibleToken {
33
34 // -----------------------------------------------------------------------
35 // Contract Events
36 // -----------------------------------------------------------------------
37
38 // Emitted when the contract is created
39 access(all) event ContractInitialized()
40
41 // Emitted when a new Edition struct is created
42 access(all) event EditionCreated(id: UInt32, name: String, printingLimit: UInt32?)
43
44 // Emitted when Edition Metadata is updated
45 access(all) event EditionMetadaUpdated(editionID: UInt32)
46
47
48 // default royalties
49 access(all) event DefaultRoyaltiesUpdated(name: String, cut: UFix64)
50
51 // remove default royalty
52 access(all) event DefaultRoyaltyRemoved(name: String)
53
54 // royalties for edition
55 access(all) event RoyaltiesForEditionUpdated(editionID: UInt32, name: String, cut: UFix64)
56
57 // remove royalty for edition
58 access(all) event RoyaltiesForEditionRemoved(editionID: UInt32, name: String)
59
60
61 // RevertRoyaltiesForEditionToDefault when the admin clears the specific royalties for that edition, to revert back to default royalties
62 access(all) event RevertRoyaltiesForEditionToDefault(editionID: UInt32)
63
64
65
66 // Emitted when a new item was minted
67 access(all) event ItemMinted(itemID:UInt64, merchantID: UInt32, editionID: UInt32, editionNumber: UInt32)
68
69 // Item related events
70 //
71 // Emitted when an Item is withdrawn from a Collection
72 access(all) event Withdraw(id: UInt64, from: Address?)
73 // Emitted when an Item is deposited into a Collection
74 access(all) event Deposit(id: UInt64, to: Address?)
75 // Emitted when an Item is destroyed
76 access(all) event ItemDestroyed(id: UInt64)
77
78
79 // Named paths
80 //
81 access(all) let CollectionStoragePath: StoragePath
82 access(all) let CollectionPublicPath: PublicPath
83 access(all) let AdminStoragePath: StoragePath
84
85
86 // -----------------------------------------------------------------------
87 // YoungBoysBern contract-level fields.
88 // These contain actual values that are stored in the smart contract.
89 // -----------------------------------------------------------------------
90
91 // Variable size dictionary of Editions resources
92 access(self) var editions: @{UInt32: Edition}
93
94
95 // the default royalties
96 access(self) var defaultRoyalties: {String: MetadataViews.Royalty}
97
98 // If a specific NFT requires their own royalties,
99 // the default royalties can be overwritten in this dictionary.
100 access(all) var royaltiesForSpecificEdition: {UInt32: {String: MetadataViews.Royalty}}
101
102
103 // The ID that is used to create Admins.
104 // Every Admins should have a unique identifier.
105 access(all) var nextAdminID: UInt32
106
107
108
109 // The ID that is used to create Editions.
110 // Every time an Edition is created, nextEditionID is assigned
111 // to the edition and then is incremented by 1.
112 access(all) var nextEditionID: UInt32
113
114
115
116 // The total number of NFTs that have been created for this smart contract
117 // Because NFTs can be destroyed, it doesn't necessarily mean that this
118 // reflects the total number of NFTs in existence, just the number that
119 // have been minted to date. Also used as global nft IDs for minting.
120 access(all) var totalSupply: UInt64
121
122 // -----------------------------------------------------------------------
123 // YoungBoysBern contract-level Composite Type definitions
124 // -----------------------------------------------------------------------
125 // These are just *definitions* for Types that this contract
126 // and other accounts can use. These definitions do not contain
127 // actual stored values, but an instance (or object) of one of these Types
128 // can be created by this contract that contains stored values.
129 // -----------------------------------------------------------------------
130
131
132 // EditionData is a struct definition to have all of the same fields as the Edition resource.
133 // it can be used to publicly read Edition data
134
135 access(all) struct EditionData {
136 access(all) let editionID: UInt32
137 access(all) let merchantID: UInt32
138 access(all) let name: String
139 access(all) var items: [UInt64]
140 access(all) var metadata: {String: String}
141 access(all) var numberOfItemsMinted: UInt32
142 access(all) var printingLimit: UInt32?
143
144 init(editionID: UInt32) {
145
146 if YoungBoysBern.editions[editionID] == nil {
147 panic("the editionID was not found")
148 }
149 let editionToRead = (&YoungBoysBern.editions[editionID] as &Edition?)!
150
151 self.editionID = editionID
152 self.metadata = (YoungBoysBern.editions[editionID]?.metadata ?? {})
153 self.merchantID = editionToRead.merchantID
154 self.name = editionToRead.name
155 self.printingLimit = editionToRead.printingLimit
156 self.numberOfItemsMinted=editionToRead.numberOfItemsMinted
157 self.items=YoungBoysBern.editions[editionID]?.items ?? []
158 }
159 }
160 // Edition is a Ressource that holds metadata associated
161 // with a specific NFT
162 //
163 // NFTs will all reference an Edition as the owner of
164 // its metadata. The Editions are publicly accessible, so anyone can
165 // read the metadata associated with a specific EditionID
166 //
167 access(all) resource Edition {
168
169 // The unique ID for the Edition
170 access(all) let editionID: UInt32
171
172 // The ID of the merchant that owns the edition
173 access(all) let merchantID: UInt32
174
175 // Stores all the metadata about the edition as a string mapping
176 // This is not the long term way NFT metadata will be stored. It's a temporary
177 // construct while we figure out a better way to do metadata.
178 //
179 access(all) let metadata: {String: String}
180
181 // Array of items that are a part of this collection.
182 // When an item is added to the collection, its ID gets appended here.
183 access(contract) var items: [UInt64]
184
185 // The number of items minted in this collection.
186 // When an item is added to the collection, the numberOfItems is incremented by 1
187 // It will be used to identify the editionNumber of an item
188 // if the edition is open (printingLimit=nil), we can keep minting new items
189 // if the edition is limited (printingLimit!=nil), we can keep minting items until we reach printingLimit
190 access(all) var numberOfItemsMinted: UInt32
191
192
193 // the limit of items that can be minted. For open editions, this value should be set to nil.
194 access(all) var printingLimit: UInt32?
195
196 // the name of the edition
197 access(all) var name: String
198
199 init(merchantID: UInt32, metadata: {String: String}, name: String, printingLimit:UInt32?) {
200 pre {
201 metadata.length != 0: "Metadata cannot be empty"
202 name!=nil: "Name is undefined"
203 }
204 self.editionID = YoungBoysBern.nextEditionID
205 self.merchantID = merchantID
206 self.metadata = metadata
207 self.name = name
208 self.printingLimit = printingLimit
209 self.numberOfItemsMinted=0
210 self.items = []
211
212
213 // Increment the ID so that it isn't used again
214 YoungBoysBern.nextEditionID = YoungBoysBern.nextEditionID + (1 as UInt32)
215
216 emit EditionCreated(id: self.editionID, name: self.name, printingLimit: self.printingLimit)
217 }
218
219
220 // mintItem mints a new Item and returns the newly minted Item
221 //
222 // Pre-Conditions:
223 // If the edition is limited the number of items minted in the edition must be strictly less than the printing limit
224 //
225 // Returns: The NFT that was minted
226 //
227 access(all) fun mintItem(): @NFT {
228 pre {
229 (self.numberOfItemsMinted < (self.printingLimit ?? (4294967295 as UInt32) )): "We have reached the printing limit for this edition"
230 }
231
232 // Gets the number of Itms that have been minted for this Edition
233 // to use as this Item's edition number
234 let numMinted = self.numberOfItemsMinted + (1 as UInt32)
235
236 // Mint the new item
237 let newItem: @NFT <- create NFT(merchantID: self.merchantID, editionID: self.editionID, editionNumber: numMinted)
238
239
240 // Add the Item to the array of items
241 self.items.append(newItem.id)
242
243 // Increment the count of Items
244 self.numberOfItemsMinted = numMinted
245
246 return <-newItem
247 }
248
249 // batchMintItems mints an arbitrary quantity of Items
250 // and returns them as a Collection
251 // Be sure there are enough
252 //
253 // Parameters: quantity: The quantity of Items to be minted
254 //
255 // Returns: Collection object that contains all the Items that were minted
256 //
257 access(all) fun batchMintItems(quantity: UInt32): @Collection {
258
259 pre {
260 ((self.numberOfItemsMinted+quantity)<=(self.printingLimit ?? (4294967295 as UInt32))): "We have reached the printing limit for this edition"
261 }
262
263 let newCollection <- create Collection()
264
265 var i: UInt32 = 0
266 while i < quantity {
267 newCollection.deposit(token: <-self.mintItem())
268 i = i + (1 as UInt32)
269 }
270
271 return <-newCollection
272 }
273
274 // updateMetadata updates the metadata
275 //
276 // Parameters:
277 //
278 // updates: a dictionary of key - values that is requested to be appended
279 //
280 // suffix: If the metadata already contains an attribute with a given key, this value should still be kept
281 // for posteriority. Therefore, the old value to be replaced will be stored in a metadata entry with key = key+suffix.
282 // This can offer some reassurance to the NFT owner that the metadata will never disappear.
283 //
284 // Returns: the EditionID
285 //
286 access(all) fun updateMetadata(updates: {String:String}, suffix: String): UInt32 {
287
288
289
290 // prevalidation
291 // if metadata[key] exists and metadata[key+suffix] exists, we have a clash.
292 for key in updates.keys {
293
294 let newKey = key.concat(suffix)
295
296 if self.metadata[key] != nil && self.metadata[newKey]!=nil {
297 var errorMsg = "attributes "
298 errorMsg = errorMsg.concat(key).concat(" and ").concat(newKey).concat(" are already defined")
299 panic(errorMsg)
300 }
301
302
303 }
304
305 // execution
306 for key in updates.keys {
307
308 let newKey = key.concat(suffix)
309
310 if self.metadata[key] != nil {
311 self.metadata[newKey] = self.metadata[key]
312 }
313 self.metadata[key] = updates[key]
314
315 }
316
317
318 emit EditionMetadaUpdated(editionID: self.editionID)
319
320 // Return the EditionID and return it
321 return self.editionID
322 }
323
324 }
325
326 // The struct representing an NFT Item data
327 access(all) struct ItemData {
328
329
330 // The ID of the merchant
331 access(all) let merchantID: UInt32
332
333 // The ID of the edition that the NFT comes from
334 access(all) let editionID: UInt32
335
336 // The number of the NFT within the edition
337 access(all) let editionNumber: UInt32
338
339
340
341 init(merchantID: UInt32, editionID: UInt32, editionNumber: UInt32) {
342 self.merchantID = merchantID
343 self.editionID = editionID
344 self.editionNumber = editionNumber
345 }
346
347 }
348
349 // The resource that represents the Item NFTs
350 //
351 access(all) resource NFT: NonFungibleToken.NFT {
352
353 // Global unique item ID
354 access(all) let id: UInt64
355
356 // Struct of the metadata
357 access(all) let data: ItemData
358
359
360 init(merchantID: UInt32, editionID: UInt32, editionNumber: UInt32) {
361
362 pre{
363 editionID > (0 as UInt32): "editionID cannot be 0"
364 editionNumber > (0 as UInt32): "editionNumber cannot be 0"
365 }
366 // Increment the global Item IDs
367 YoungBoysBern.totalSupply = YoungBoysBern.totalSupply + (1 as UInt64)
368
369 self.id = YoungBoysBern.totalSupply
370
371 // Set the metadata struct
372 self.data = ItemData(merchantID: merchantID, editionID: editionID, editionNumber: editionNumber)
373
374
375 emit ItemMinted(itemID: self.id, merchantID: merchantID, editionID: editionID, editionNumber: editionNumber)
376 }
377
378 access(all) view fun getData(): ItemData {
379 return self.data
380 }
381
382 /// createEmptyCollection creates an empty Collection
383 /// and returns it to the caller so that they can own NFTs
384 /// @{NonFungibleToken.Collection}
385 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
386 return <-YoungBoysBern.createEmptyCollection(nftType: Type<@YoungBoysBern.NFT>())
387 }
388
389
390 access(all) view fun getViews(): [Type] {
391 return [
392 Type<MetadataViews.Display>(),
393 Type<MetadataViews.Royalties>(),
394 Type<MetadataViews.Editions>(),
395 Type<MetadataViews.ExternalURL>(),
396 Type<MetadataViews.NFTCollectionData>(),
397 Type<MetadataViews.NFTCollectionDisplay>(),
398 Type<MetadataViews.Serial>(),
399 Type<MetadataViews.Traits>()
400 ]
401 }
402
403
404 access(all) fun resolveView(_ view: Type): AnyStruct? {
405 switch view {
406 case Type<MetadataViews.Display>():
407 let edition = EditionData(editionID: self.data.editionID);
408 return MetadataViews.Display(
409 name: edition.name,
410 description: edition.metadata["description"] ?? "",
411 thumbnail: MetadataViews.HTTPFile(
412 url: edition.metadata["thumbnail"] ?? ""
413 )
414 )
415 case Type<MetadataViews.Editions>():
416 let edition = EditionData(editionID: self.data.editionID);
417 let maxNumber = edition.printingLimit ?? nil
418 var max: UInt64? = nil;
419 if maxNumber != nil {
420 max = UInt64(maxNumber!)
421 }
422
423
424 let editionInfo = MetadataViews.Edition(name: edition.name, number: UInt64(self.data.editionNumber), max:max)
425 let editionList: [MetadataViews.Edition] = [editionInfo]
426 return MetadataViews.Editions(
427 editionList
428 )
429
430 case Type<MetadataViews.ExternalURL>():
431 return MetadataViews.ExternalURL("https://www.bscyb.ch/")
432
433
434 case Type<MetadataViews.Royalties>():
435 let royaltiesDictionary = YoungBoysBern.royaltiesForSpecificEdition[self.data.editionID] ?? YoungBoysBern.defaultRoyalties
436 var royalties: [MetadataViews.Royalty] = []
437 for royaltyName in royaltiesDictionary.keys {
438 royalties.append(royaltiesDictionary[royaltyName]!)
439 }
440 return MetadataViews.Royalties(royalties)
441
442 case Type<MetadataViews.NFTCollectionDisplay>():
443 return MetadataViews.NFTCollectionDisplay(
444 name: "YoungBoysBern",
445 description: "YoungBoysBern Contract",
446 externalURL: MetadataViews.ExternalURL("https://www.bscyb.ch/"),
447 squareImage: MetadataViews.Media(
448 file: MetadataViews.HTTPFile(
449 url: "https://dkuxa1i6sgo8h.cloudfront.net/BSC-Young-Boys/BSC_Young_Boys_logo.png"
450 ),
451 mediaType: "image/png"
452 ),
453 bannerImage: MetadataViews.Media(
454 file: MetadataViews.HTTPFile(
455 url: "https://dkuxa1i6sgo8h.cloudfront.net/BSC-Young-Boys/BSC_Young_Boys_logo.png"
456 ),
457 mediaType: "image/png"
458 ),
459 socials: {}
460 )
461
462 case Type<MetadataViews.NFTCollectionData>():
463 return MetadataViews.NFTCollectionData(
464 storagePath: YoungBoysBern.CollectionStoragePath,
465 publicPath: YoungBoysBern.CollectionPublicPath,
466 publicCollection: Type<&YoungBoysBern.Collection>(),
467 publicLinkedType: Type<&YoungBoysBern.Collection>(),
468 createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
469 return <-YoungBoysBern.createEmptyCollection(nftType: Type<@YoungBoysBern.NFT>())
470 })
471 )
472
473
474 case Type<MetadataViews.Traits>():
475 // exclude essential metadata to keep unique traits
476 let excludedTraits = ["name", "description", "externalUrl", "squareImage", "squareImageType", "bannerImage", "bannerImageType", "thumbnail", "thumbnailType"]
477
478 let edition = EditionData(editionID: self.data.editionID);
479 let metadata = edition.metadata;
480
481 metadata.insert(key: "editionID", edition.editionID.toString());
482 metadata.insert(key: "merchantID", edition.merchantID.toString());
483
484 let traitsView = MetadataViews.dictToTraits(dict: metadata, excludedNames: excludedTraits)
485
486 return traitsView
487
488
489 case Type<MetadataViews.Serial>():
490 return MetadataViews.Serial(
491 UInt64(self.data.editionNumber)
492 )
493
494
495 }
496
497 return nil
498 }
499
500
501
502
503 }
504
505 access(all) view fun getContractViews(resourceType: Type?): [Type] {
506 return []
507 }
508
509 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
510
511 return nil
512 }
513
514
515 // Admin is a special authorization resource that
516 // allows the owner to perform important functions to modify the
517 // various aspects of the Editions and Items
518 //
519 access(all) resource Admin {
520
521 access(all) let id: UInt32
522 // createEdition creates a new Edition struct
523 // and stores it in the Editions dictionary in the contract
524 //
525
526 init(id: UInt32) {
527 self.id = id
528 }
529
530 // createEdition creates a new Edition resource and stores it
531 // in the editions mapping in the contract
532 //
533 // Parameters:
534 // merchantID: The ID of the merchant
535 // metadata: the associated data
536 // name: The name of the Edition
537 // printingLimit: We can only mint this quantity of NFTs. If printingLimit is nil there is no limit (theoretically UInt32.max)
538 //
539 access(all) fun createEdition(merchantID: UInt32, metadata: {String: String}, name: String, printingLimit: UInt32?) {
540 // Create the new Edition
541 var newEdition <- create Edition(merchantID:merchantID, metadata: metadata, name: name, printingLimit:printingLimit)
542 let newID = newEdition.editionID
543
544 // Store it in the contract storage
545 YoungBoysBern.editions[newID] <-! newEdition
546
547 }
548
549
550 // borrowEdition returns a reference to an edition in the YoungBoysBern
551 // contract so that the admin can call methods on it
552 //
553 // Parameters: editionID: The ID of the Edition that you want to
554 // get a reference to
555 //
556 // Returns: A reference to the Edition with all of the fields
557 // and methods exposed
558 //
559 access(all) fun borrowEdition(editionID: UInt32): &Edition {
560 pre {
561 YoungBoysBern.editions[editionID] != nil: "Cannot borrow Edition: it does not exist"
562 }
563
564 // Get a reference to the Edition and return it
565 return (&YoungBoysBern.editions[editionID] as &Edition?)!
566 }
567
568 // updateEditionMetadata returns a reference to an edition in the YoungBoysBern
569 // contract so that the admin can call methods on it
570 //
571 // Parameters:
572 // editionID: The ID of the Edition that you want to update
573 //
574 // updates: a dictionary of key - values that is requested to be appended
575 //
576 // suffix: If the metadata already contains an attribute with a given key, this value should still be kept
577 // for posteriority. Therefore, the old value to be replaced will be stored in a metadata entry with key = key+suffix.
578 // This can offer some reassurance to the NFT owner that the metadata will never disappear.
579 //
580 // Returns: the EditionID
581 //
582 access(all) fun updateEditionMetadata(editionID: UInt32, updates: {String:String}, suffix: String): UInt32 {
583 pre {
584 YoungBoysBern.editions[editionID] != nil: "Cannot borrow Edition: it does not exist"
585 }
586
587 let editionRef = &YoungBoysBern.editions[editionID] as &Edition?
588 editionRef!.updateMetadata(updates: updates, suffix: suffix)
589
590 // Return the EditionID and return it
591 return editionID
592 }
593
594
595 // set default royalties
596 access(all) fun setDefaultRoyaltyByName(name: String, royalty: MetadataViews.Royalty) {
597 YoungBoysBern.defaultRoyalties[name] = royalty;
598 // verify total
599 let totalCut = YoungBoysBern.getDefaultRoyaltyTotalRate()
600 assert(totalCut <= 1.0, message: "Sum of cutInfos multipliers should not be greater than 1.0")
601 emit DefaultRoyaltiesUpdated(name: name, cut: royalty.cut)
602 }
603
604 access(all) fun removeDefaultRoyaltyByName(name: String) {
605
606 if !YoungBoysBern.defaultRoyalties.containsKey(name) {
607
608 var errorMsg = "Default Royalty with name ["
609 errorMsg = errorMsg.concat(name).concat("] does not exist")
610 panic(errorMsg)
611 }
612
613 YoungBoysBern.defaultRoyalties.remove(key: name);
614 emit DefaultRoyaltyRemoved(name: name)
615 }
616
617 // set royalties for edition
618 access(all) fun setEditionRoyaltyByName( editionID: UInt32, name: String, royalty: MetadataViews.Royalty) {
619
620 if !YoungBoysBern.royaltiesForSpecificEdition.containsKey(editionID) {
621 YoungBoysBern.royaltiesForSpecificEdition.insert(key:editionID, {})
622 }
623 //let royaltiesForSpecificEdition = YoungBoysBern.royaltiesForSpecificEdition[editionID]!
624 //royaltiesForSpecificEdition.insert(key: name, royalty);
625 YoungBoysBern.royaltiesForSpecificEdition[editionID]!.insert(key: name, royalty);
626 let totalCut = YoungBoysBern.getEditionRoyaltyTotalRate(editionID:editionID)
627 assert(totalCut <= 1.0, message: "Sum of cutInfos multipliers should not be greater than 1.0")
628
629 emit RoyaltiesForEditionUpdated(editionID: editionID, name: name, cut: royalty.cut)
630 }
631
632 // remove royalty for edition
633 access(all) fun removeEditionRoyaltyByName( editionID: UInt32, name: String) {
634
635 if !YoungBoysBern.royaltiesForSpecificEdition.containsKey(editionID) {
636 var errorMsg = "Royalty specific to editionID"
637 errorMsg = errorMsg.concat(editionID.toString()).concat(" does not exist")
638 panic(errorMsg)
639 }
640 let royaltiesForSpecificEdition = YoungBoysBern.royaltiesForSpecificEdition[editionID]!
641 if !royaltiesForSpecificEdition.containsKey(name) {
642 var errorMsg = "Royalty specific to editionID"
643 errorMsg = errorMsg.concat(editionID.toString()).concat(" with the name[").concat(name).concat("] does not exist")
644 panic(errorMsg)
645 }
646 YoungBoysBern.royaltiesForSpecificEdition[editionID]!.remove(key: name);
647 emit RoyaltiesForEditionRemoved(editionID: editionID, name: name)
648 }
649
650 access(all) fun revertRoyaltiesForEditionToDefault(editionID: UInt32){
651 if !YoungBoysBern.royaltiesForSpecificEdition.containsKey(editionID){
652 var errorMsg = "Royalty for editionID "
653 errorMsg = errorMsg.concat(editionID.toString()).concat(" does not exist")
654 panic(errorMsg)
655 }
656
657
658 YoungBoysBern.royaltiesForSpecificEdition.remove(key: editionID)
659 emit RevertRoyaltiesForEditionToDefault(editionID: editionID)
660 }
661
662
663
664
665 // createNewAdmin creates a new Admin resource
666 //
667 access(all) fun createNewAdmin(): @Admin {
668
669
670 let newID = YoungBoysBern.nextAdminID
671 // Increment the ID so that it isn't used again
672 YoungBoysBern.nextAdminID = YoungBoysBern.nextAdminID + (1 as UInt32)
673
674 return <-create Admin(id: newID)
675 }
676
677 }
678
679
680
681 // This is the interface that users can cast their YoungBoysBern Collection as
682 // to allow others to deposit YoungBoysBerns into their Collection. It also allows for reading
683 // the IDs of YoungBoysBerns in the Collection.
684 access(all) resource interface YoungBoysBernCollectionPublic {
685
686
687 }
688
689
690
691 // Collection is a resource that every user who owns NFTs
692 // will store in their account to manage their NFTS
693 //
694 access(all) resource Collection: NonFungibleToken.Collection, YoungBoysBernCollectionPublic {
695 // Dictionary of YoungBoysBern conforming tokens
696 // NFT is a resource type with a UInt64 ID field
697 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
698 // access(all) var ownedNFTs: @{UInt64: YoungBoysBern.NFT}
699
700 init() {
701 self.ownedNFTs <- {}
702 }
703
704
705
706
707 // withdraw removes a YoungBoysBern from the Collection and moves it to the caller
708 //
709 // Parameters: withdrawID: The ID of the NFT
710 // that is to be removed from the Collection
711 //
712 // returns: @NonFungibleToken.NFT the token that was withdrawn
713 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
714
715 // Remove the nft from the Collection
716 let token <- self.ownedNFTs.remove(key: withdrawID)
717 ?? panic("Cannot withdraw: YoungBoysBern does not exist in the collection")
718
719 emit Withdraw(id: token.id, from: self.owner?.address)
720
721 // Return the withdrawn token
722 return <-token
723 }
724
725 // batchWithdraw withdraws multiple tokens and returns them as a Collection
726 //
727 // Parameters: ids: An array of IDs to withdraw
728 //
729 // Returns: @NonFungibleToken.Collection: A collection that contains
730 // the withdrawn YoungBoysBern items
731 //
732 access(NonFungibleToken.Withdraw) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
733 // Create a new empty Collection
734 var batchCollection <- create Collection()
735
736 // Iterate through the ids and withdraw them from the Collection
737 for id in ids {
738
739 let token <-self.withdraw(withdrawID: id)
740
741 batchCollection.deposit(token: <-token)
742 }
743
744 // Return the withdrawn tokens
745 return <-batchCollection
746 }
747
748 /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
749 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
750 let supportedTypes: {Type: Bool} = {}
751 supportedTypes[Type<@YoungBoysBern.NFT>()] = true
752 return supportedTypes
753 }
754
755 /// Returns whether or not the given type is accepted by the collection
756 /// A collection that can accept any type should just return true by default
757 access(all) view fun isSupportedNFTType(type: Type): Bool {
758 return type == Type<@YoungBoysBern.NFT>()
759 }
760
761 /// Gets the amount of NFTs stored in the collection
762 access(all) view fun getLength(): Int {
763 return self.ownedNFTs.length
764 }
765
766 // deposit takes a YoungBoysBern and adds it to the Collections dictionary
767 //
768 // Paramters: token: the NFT to be deposited in the collection
769 //
770 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
771
772 // Cast the deposited token as a YoungBoysBern NFT to make sure
773 // it is the correct type
774 let token <- token as! @YoungBoysBern.NFT
775
776 // Get the token's ID
777 let id = token.id
778
779 // Add the new token to the dictionary
780 let oldToken <- self.ownedNFTs[id] <- token
781
782 // Only emit a deposit event if the Collection
783 // is in an account's storage
784 if self.owner?.address != nil {
785 emit Deposit(id: id, to: self.owner?.address)
786 }
787
788 // Destroy the empty old token that was "removed"
789 destroy oldToken
790 }
791
792 // batchDeposit takes a Collection object as an argument
793 // and deposits each contained NFT into this Collection
794 access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection}) {
795
796 // Get an array of the IDs to be deposited
797 let keys = tokens.getIDs()
798
799 // Iterate through the keys in the collection and deposit each one
800 for key in keys {
801 self.deposit(token: <-tokens.withdraw(withdrawID: key))
802 }
803
804 // Destroy the empty Collection
805 destroy tokens
806 }
807
808 // getIDs returns an array of the IDs that are in the Collection
809 access(all) view fun getIDs(): [UInt64] {
810 return self.ownedNFTs.keys
811 }
812
813 // borrowNFT Returns a borrowed reference to a YoungBoysBern in the Collection
814 // so that the caller can read its ID
815 //
816 // Parameters: id: The ID of the NFT to get the reference for
817 //
818 // Returns: A reference to the NFT
819 //
820 // Note: This only allows the caller to read the ID of the NFT,
821 // not any YoungBoysBern specific data. Please use borrowYoungBoysBern to
822 // read YoungBoysBern data.
823 //
824 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
825 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
826 }
827
828
829
830
831 /// Borrow the view resolver for the specified NFT ID
832 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
833 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
834 return nft as &{ViewResolver.Resolver}
835 }
836 return nil
837 }
838
839 /// createEmptyCollection creates an empty Collection of the same type
840 /// and returns it to the caller
841 /// @return A an empty collection of the same type
842 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
843 return <-YoungBoysBern.createEmptyCollection(nftType: Type<@YoungBoysBern.NFT>())
844 }
845 }
846
847
848
849 // -----------------------------------------------------------------------
850 // YoungBoysBern contract-level function definitions
851 // -----------------------------------------------------------------------
852
853 // createEmptyCollection creates a new, empty Collection object so that
854 // a user can store it in their account storage.
855 // Once they have a Collection in their storage, they are able to receive
856 // YoungBoysBerns in transactions.
857 //
858 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
859 return <- create YoungBoysBern.Collection()
860 }
861
862 access(all) fun createEmptyYoungBoysBernCollection(): @YoungBoysBern.Collection {
863 return <-create YoungBoysBern.Collection()
864 }
865
866 // getDefaultRoyalties returns the default royalties
867 access(all) fun getDefaultRoyalties(): {String: MetadataViews.Royalty} {
868 return self.defaultRoyalties
869 }
870
871 // getDefaultRoyalties returns the default royalties
872 access(all) fun getDefaultRoyaltyNames(): [String] {
873 return self.defaultRoyalties.keys
874 }
875
876 // getDefaultRoyalties returns the default royalties
877 access(all) fun getDefaultRoyaltyByName(name: String): MetadataViews.Royalty? {
878 return self.defaultRoyalties[name]
879 }
880
881 // getDefaultRoyalties returns the default royalties total rate
882 access(all) fun getDefaultRoyaltyTotalRate(): UFix64 {
883 var cut = 0.0
884 for name in self.defaultRoyalties.keys {
885 cut=cut+self.defaultRoyalties[name]!.cut
886 }
887
888 return cut;
889 }
890
891
892
893
894
895 // getRoyaltiesForEdition returns the royalties set for a specific edition, that overrides the default
896 access(all) fun getEditionRoyalties(editionID: UInt32): {String: MetadataViews.Royalty} {
897 return self.royaltiesForSpecificEdition[editionID] ?? self.defaultRoyalties
898 }
899
900 // getRoyaltiesForEdition returns the royalties set for a specific edition, that overrides the default
901 access(all) fun getEditionRoyaltyNames(editionID: UInt32): [String] {
902 let royalties = YoungBoysBern.getEditionRoyalties(editionID: editionID)
903 return royalties.keys
904 }
905
906 // getRoyaltiesForEdition returns the royalties set for a specific edition, that overrides the default
907 access(all) fun getEditionRoyaltyByName(editionID: UInt32, name: String): MetadataViews.Royalty {
908 let royaltiesForSpecificEdition = YoungBoysBern.getEditionRoyalties(editionID: editionID)
909 return royaltiesForSpecificEdition[name]!
910 }
911
912 // getDefaultRoyalties returns the default royalties total rate
913 access(all) fun getEditionRoyaltyTotalRate(editionID: UInt32): UFix64 {
914 let royalties = YoungBoysBern.getEditionRoyalties(editionID: editionID)
915 var cut = 0.0
916 for name in royalties.keys {
917 cut=cut+royalties[name]!.cut
918 }
919
920 return cut;
921 }
922
923
924 // -----------------------------------------------------------------------
925 // initialization function
926 // -----------------------------------------------------------------------
927 //
928 init() {
929 // Initialize contract fields
930 self.editions <- {}
931 self.nextEditionID = 1
932 self.totalSupply = 0
933 self.defaultRoyalties = {}
934 self.royaltiesForSpecificEdition = {}
935
936
937 self.CollectionStoragePath = /storage/YoungBoysBernCollection
938 self.CollectionPublicPath = /public/YoungBoysBernCollection
939 self.AdminStoragePath = /storage/YoungBoysBernItemAdmin
940
941 // Put a new Collection in storage
942 self.account.storage.save<@Collection>(<- create Collection(), to: self.CollectionStoragePath)
943
944 // Create a public capability for the Collection
945 // self.account.link<&{YoungBoysBernCollectionPublic}>(self.CollectionPublicPath, target: self.CollectionStoragePath)
946 let collectionCap = self.account.capabilities.storage.issue<&YoungBoysBern.Collection>(self.CollectionStoragePath)
947 self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
948
949 // Put the admin ressource in storage
950 self.account.storage.save<@Admin>(<- create Admin(id: 1), to: self.AdminStoragePath)
951 self.nextAdminID = 2
952
953 emit ContractInitialized()
954 }
955
956
957}
958
959