Smart Contract

BBCollectables

A.a82aacd7c5fbb320.BBCollectables

Valid From

133,876,993

Deployed

6d ago
Feb 21, 2026, 09:13:16 PM UTC

Dependents

4 imports
1// import NonFungibleToken from 0x631e88ae7f1d7c20
2// import MetadataViews from 0x631e88ae7f1d7c20
3// import FungibleToken from 0x9a0766d93b6608b7
4// import PRNG from 0x2bf5575475144be3
5
6import NonFungibleToken from 0x1d7e57aa55817448
7import MetadataViews from 0x1d7e57aa55817448
8import ViewResolver from 0x1d7e57aa55817448
9
10// import "FungibleToken"
11
12// import "PRNG"
13
14access(all) contract BBCollectables: NonFungibleToken {
15
16    // pub let network: String
17
18    access(all) event ContractInitialized()
19
20    access(all) event CardCreated(cardID: UInt32, metadata: {String:String})
21    access(all) event PackCreated(packID: UInt32)
22
23    access(all) event CardAddedToPack(packID: UInt32, cardID: UInt32)
24    access(all) event CardRetiredFromPack(packID: UInt32, cardID: UInt32, numCards: UInt32)
25    access(all) event CardUnretiredFromPack(packID: UInt32, cardID: UInt32, numCards: UInt32)
26    access(all) event CardFinalized(cardID: UInt32)
27    access(all) event CardUnfinalized(cardID: UInt32)
28
29    access(all) event PackLocked(packID: UInt32)
30    access(all) event PackUnlocked(packID: UInt32)
31
32    access(all) event BlockPackMint(packID: UInt32)
33
34   
35    access(all) event TicketMinted(packID: UInt32, serialNumber: UInt32) 
36    access(all) event TicketSpent(id: UInt64, packID: UInt32, serialNumber: UInt32, content: [UInt32])
37    access(all) event TicketDestroyed(id: UInt64)
38
39    access(all) event BBNftMinted(BBNftID: UInt64, cardID: UInt32, packID: UInt32, serialNumber: UInt32)
40    access(all) event BBNftDestroyed(id: UInt64)
41
42
43    access(all) event Withdraw(id: UInt64, from: Address?)
44
45    access(all) event Deposit(id: UInt64, to: Address?)
46
47    access(all) let OrganiserName: String
48
49    access(all) let OrganiserStringIdentifier: String
50
51    access(all) let NftCollectionStoragePath: StoragePath
52    access(all) let NftCollectionPublicPath: PublicPath
53    access(all) let AdminStoragePath: StoragePath
54
55    access(self) var cardDatas: {UInt32: Card}
56    
57    access(self) var packDatas: {UInt32: PackData}
58    access(self) var packs: @{UInt32: Pack}
59
60    access(all) var nextCardID: UInt32
61    access(all) var nextPackID: UInt32
62
63    access(all) var totalSupply: UInt64
64
65
66    access(all) struct Card {
67
68        access(all) let cardID: UInt32
69
70        access(all) var metadata: {String: String}
71
72        access(all) var packID: UInt32
73
74        access(all) var final: Bool
75
76        access(all) var finalizedTimestamp: UFix64
77
78        init(metadata: {String: String}) {
79            pre {
80                metadata.length != 0: "New Card metadata cannot be empty"
81            }
82            self.cardID = BBCollectables.nextCardID
83            // self.categories = categories
84            self.metadata = metadata
85
86            self.packID = 0
87
88            self.final = false
89
90            self.finalizedTimestamp = 0.0
91        }
92
93        access(contract) fun updateMetadata(newMetadata: {String: String}) {
94            pre {
95                self.final == false: "Cannot eddit finalized cards"
96            }
97            self.metadata = newMetadata // Modify the dictionary directly
98        }
99
100        access(contract) fun assignToPack(packID: UInt32) {
101            pre {
102                self.packID == 0: "Card already asigned to a pack"
103            }
104            self.packID = packID // Modify the dictionary directly
105        }
106
107        access(contract) fun finalizeCard() {
108            pre {
109                self.final == false: "Card already finalized"
110            }
111
112                self.final = true
113
114                self.finalizedTimestamp = getCurrentBlock().timestamp
115
116                emit CardFinalized(cardID: self.cardID)
117        }
118
119        access(contract) fun unfinalizeCard() {
120            pre {
121                self.final == true: "Card not yet finalized"
122                getCurrentBlock().timestamp - self.finalizedTimestamp < 604800.0: "Grace period expired"
123            }
124
125                self.final = false
126                self.finalizedTimestamp = 0.0
127
128                emit CardFinalized(cardID: self.cardID)
129        }
130    }
131
132    access(all) struct PackData {
133
134        access(all) let packID: UInt32
135
136        access(all) let name: String
137
138        init(name: String) {
139            pre {
140                name.length > 0: "New Pack name cannot be empty"
141            }
142            self.packID = BBCollectables.nextPackID
143            self.name = name
144        }
145    }
146
147    access(all) resource Pack {
148
149        access(all) var name: String
150
151        access(all) let packID: UInt32
152
153        // access(contract) var rarityDistribution: {String: UInt16}
154
155        access(contract) var cards: [UInt32]
156
157        access(contract) var retired: {UInt32: Bool}
158
159        access(all) var locked: Bool
160
161        access(contract) var mintCapPerCard: {UInt32: UInt32}
162
163        access(contract) var numberMintedPerCard: {UInt32: UInt32}
164
165        init(name: String) {
166            self.name = name
167            self.packID = BBCollectables.nextPackID
168            self.cards = []
169            self.retired = {}
170            self.locked = false
171            self.mintCapPerCard = {}
172            self.numberMintedPerCard = {}
173
174            BBCollectables.packDatas[self.packID] = PackData(name: name)
175        }
176
177        access(all) fun changeName(newName: String){
178            self.name = newName
179        }
180
181        access(all) fun addCard(cardID: UInt32, mintCap: UInt32) {
182
183            pre {
184                BBCollectables.cardDatas[cardID] != nil: "Cannot add the Card to Pack: Card doesn't exist"
185                !self.locked: "Cannot add the Card to the Pack after the Pack has been locked"
186                self.numberMintedPerCard[cardID] == nil: "The Card has already beed added to the Pack"
187                mintCap >= 0: "'maxCount' parameter must be 0 or higher"
188            }
189
190            self.cards.append(cardID)
191
192            self.retired[cardID] = false
193
194            self.mintCapPerCard[cardID] = mintCap
195
196            self.numberMintedPerCard[cardID] = 0
197
198            BBCollectables.cardDatas[cardID]!.assignToPack(packID: self.packID)
199
200            emit CardAddedToPack(packID: self.packID, cardID: cardID)
201        }
202
203        access(all) fun addCards(cardIDs: [UInt32], mintCaps: [UInt32]) {
204            
205            var i = 0
206            while i < cardIDs.length {
207                self.addCard(cardID: cardIDs[i], mintCap: mintCaps[i])
208                i = i + 1
209            }
210        }
211        
212
213        access(all) fun retireCard(cardID: UInt32) {
214            pre {
215                self.retired[cardID] != nil: "Cannot retire the Card: Card doesn't exist in this Pack!"
216                self.retired[cardID] == false: "Cannot retire the Card: Card already retired!"
217                !self.locked: "Cannot retire card: This Pack is locked."
218            }
219            
220
221            if !self.retired[cardID]! {
222                self.retired[cardID] = true
223
224                emit CardRetiredFromPack(packID: self.packID, cardID: cardID, numCards: self.numberMintedPerCard[cardID]!)
225            }
226        }
227
228        access(all) fun unretireCard(cardID: UInt32) {
229            pre {
230                self.retired[cardID] != nil: "Cannot unretire the Card: Card doesn't exist in this Pack!"
231                self.retired[cardID] == true: "Cannot unretire the Card: Card must be retired!"
232                self.numberMintedPerCard[cardID]! < self.mintCapPerCard[cardID]! || self.mintCapPerCard[cardID]! == 0: "Cannot unretire the Card: Card has reached it's maximum mint cap!"
233                !self.locked: "Cannot unretire card: This Pack is locked."
234            }
235
236            if self.retired[cardID]! {
237                self.retired[cardID] = false
238
239                emit CardUnretiredFromPack(packID: self.packID, cardID: cardID, numCards: self.numberMintedPerCard[cardID]!)
240            }
241        }
242
243        access(all) fun retireAll() {
244            for card in self.cards {
245                self.retireCard(cardID: card)
246            }
247        }
248
249        access(all) fun lock() {
250            if !self.locked {
251                self.locked = true
252                emit PackLocked(packID: self.packID)
253            }
254        }
255
256        access(all) fun unlock() {
257            if self.locked {
258                self.locked = false
259                emit PackUnlocked(packID: self.packID)
260            }
261        }
262
263        access(all) fun mintBBNft(cardID: UInt32): @NFT {
264            pre {
265                self.retired[cardID] != nil: "Cannot mint the Card: This Card doesn't exist."
266                !self.retired[cardID]!: "Cannot mint the Card from this Pack: This Card has been retired."
267                self.numberMintedPerCard[cardID]! < self.mintCapPerCard[cardID]! || self.mintCapPerCard[cardID]! == 0: "Card has reached the maximum mint cap."
268            }
269
270            let numInCard = self.numberMintedPerCard[cardID]!
271
272            let newBBNft: @NFT <- create NFT(cardID: cardID,
273                                              packID: self.packID,
274                                              timestamp: getCurrentBlock().timestamp,
275                                              packIndex: UInt32(self.cards.firstIndex(of: cardID)!),
276                                              serialNumber: numInCard +1
277                                              )
278            
279            self.numberMintedPerCard[cardID] = numInCard +1
280
281            if self.numberMintedPerCard[cardID]! >= self.mintCapPerCard[cardID]! && self.mintCapPerCard[cardID]! > 0 {
282                self.retireCard(cardID: cardID)
283            }
284
285            return <- newBBNft
286        }
287
288        access(all) fun batchMintBBNft(cardID: UInt32, quantity: UInt64): @Collection {
289            let newCollection <- create Collection()
290
291            var i: UInt64 = 0
292            while i < quantity {
293                newCollection.deposit(token: <-self.mintBBNft(cardID: cardID))
294                i = i +1
295            }
296
297            return <-newCollection
298        }
299
300        access(all) fun getCards(): [UInt32] {
301            return self.cards
302        }
303
304        access(all) fun getRetired(): {UInt32: Bool} {
305            return self.retired
306        }
307
308        access(all) fun getNumMintedPerCard(): {UInt32: UInt32} {
309            return self.numberMintedPerCard
310        }
311    }
312
313    access(all) struct QueryPackData {
314        access(all) let packID: UInt32
315        access(all) let name: String
316        access(all) var locked: Bool
317        access(self) var cards: &[UInt32]
318        access(self) var retired: &{UInt32: Bool}
319        access(self) var mintCapPerCard: &{UInt32: UInt32}
320        access(self) var numberMintedPerCard: &{UInt32: UInt32}
321        
322
323        init(packID: UInt32) {
324            pre {
325                BBCollectables.packs[packID] != nil: "The Pack with the provided ID does not exist"
326            }
327
328            let pack= (&BBCollectables.packs[packID] as &Pack?)!
329            let packData = BBCollectables.packDatas[packID]!
330
331            self.packID = packID
332            self.name = packData.name
333            self.locked = pack.locked
334            self.cards = pack.cards
335            self.retired = pack.retired
336            self.mintCapPerCard = pack.mintCapPerCard
337            self.numberMintedPerCard = pack.numberMintedPerCard
338        }
339
340        access(all) fun getCards(): &[UInt32] {
341            return self.cards
342        }
343
344        access(all) fun getRetired(): &{UInt32: Bool} {
345            return self.retired
346        }
347
348        access(all) fun getNumberMintedPerCard(): &{UInt32: UInt32} {
349            return self.numberMintedPerCard
350        }
351    }
352
353    access(all) struct BBNftData {
354
355        access(all) let cardID: UInt32
356
357        access(all) let packID: UInt32
358
359        access(all) let timestamp: UFix64
360
361        access(all) let packIndex: UInt32
362
363        access(all) let serialNumber: UInt32
364
365
366        init(cardID: UInt32, packID: UInt32, timestamp: UFix64, packIndex:UInt32, serialNumber: UInt32) {
367            self.cardID = cardID
368            self.packID = packID
369            self.timestamp = timestamp
370            self.packIndex = packIndex
371            self.serialNumber = serialNumber
372        }
373    }
374
375    access(all) struct CustomMetadataView {
376
377        access(all) let fields: {String: AnyStruct}
378
379        init(fields: {String: AnyStruct}) {
380            self.fields = fields
381        } 
382    }
383
384    access(all) resource NFT: NonFungibleToken.NFT {
385
386        access(all) let id: UInt64
387        access(all) let data: BBNftData
388
389        init(cardID: UInt32, packID: UInt32, timestamp: UFix64, packIndex: UInt32, serialNumber: UInt32) {
390
391            BBCollectables.totalSupply = BBCollectables.totalSupply +1
392
393            self.id = BBCollectables.totalSupply
394
395            self.data = BBNftData(cardID: cardID, packID: packID, timestamp: timestamp, packIndex: packIndex, serialNumber: serialNumber)
396
397            emit BBNftMinted(BBNftID: self.id, cardID: cardID, packID: self.data.packID, serialNumber: self.data.serialNumber)
398        }
399        access(all) event ResourceDestroyed(
400            id: UInt64 = self.id,
401            serialNumber: UInt32 = self.data.serialNumber,
402            cardID: UInt32 =  self.data.cardID,
403            packID: UInt32 = self.data.packID
404        )
405
406        access(all) fun name(): String {
407            let cardName: String = BBCollectables.getCardMetaDataByField(cardID: self.data.cardID, field: "name") ?? ""
408            return cardName
409        }
410
411        access(all) fun description(): String {
412            let packName: String = BBCollectables.getPackName(packID: self.data.packID) ?? ""
413            let serialNumber: String = self.data.serialNumber.toString()
414            return "A pack "
415                .concat(packName)
416                .concat(" BBNft with serial number ")
417                .concat(serialNumber)
418        }
419
420        access(all) fun getCardURL(): String {
421            return "https://media.staging.bluebite.tech/".concat(BBCollectables.OrganiserStringIdentifier).concat("/collectables/cards/").concat(BBCollectables.getCardMetaDataByField(cardID: self.data.cardID, field: "name")!).concat("/image.png")
422        }
423
424        access(all) fun getCardVideoURL(): String {
425            return "https://media.staging.bluebite.tech/".concat(BBCollectables.OrganiserStringIdentifier).concat("/collectables/cards/").concat(BBCollectables.getCardMetaDataByField(cardID: self.data.cardID, field: "name")!).concat("/video.mp4")
426        }
427
428        access(all) fun mapCardData(dict: {String: AnyStruct}) : {String: AnyStruct} {      
429            let cardMetadata = BBCollectables.getCardMetaData(cardID: self.data.cardID) ?? {}
430            for name in cardMetadata.keys {
431                let value = cardMetadata[name] ?? ""
432                if value != "" {
433                    dict.insert(key: name, value)
434                }
435            }
436            return dict
437        }
438
439        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
440            return <- BBCollectables.createEmptyCollection(nftType: Type<@BBCollectables.NFT>())
441        }
442
443        access(all) view fun getViews(): [Type] {
444            return [
445                Type<MetadataViews.Display>(),
446                Type<CustomMetadataView>(),
447                // Type<MetadataViews.ExternalURL>(),
448                // Type<MetadataViews.NFTCollectionData>(),
449                // Type<MetadataViews.NFTCollectionDisplay>(),
450                Type<MetadataViews.Serial>(),
451                Type<MetadataViews.Traits>(),
452                Type<MetadataViews.Medias>()
453            ]
454        }
455
456        access(all) fun resolveView(_ view: Type): AnyStruct? {
457            switch view {
458                case Type<MetadataViews.Display>():
459                    return MetadataViews.Display(
460                        name: self.name(),
461                        description: self.description(),
462                        thumbnail: MetadataViews.HTTPFile(url: self.getCardURL())
463                    )
464                case Type<CustomMetadataView>():
465                    return CustomMetadataView(
466                        fields: {
467                            "name": BBCollectables.getCardMetaDataByField(cardID: self.data.cardID, field: "name"),
468                            "description": BBCollectables.getCardMetaDataByField(cardID: self.data.cardID, field: "description"),
469                            "rarity": BBCollectables.getCardMetaDataByField(cardID: self.data.cardID, field: "rarity"),
470                            "date": BBCollectables.getCardMetaDataByField(cardID: self.data.cardID, field: "date"),
471
472                            "media": [
473                                MetadataViews.Media(
474                                    file: MetadataViews.HTTPFile(
475                                        url: self.getCardURL()
476                                    ),
477                                    mediaType: "image/png"
478                                ),
479                                MetadataViews.Media(
480                                    file: MetadataViews.HTTPFile(
481                                        url: self.getCardVideoURL()
482                                    ),
483                                    mediaType: "video/mp4"
484                                )
485                            ],
486                            
487                            "cardID": self.data.cardID,
488                            "packID": self.data.packID,
489                            "timestamp": self.data.timestamp,
490                            "packIndex": self.data.packIndex,
491                            "serialNumber": self.data.serialNumber,
492                            "numberMinted": BBCollectables.getPackData(packID: self.data.packID)!.getNumberMintedPerCard()[self.data.cardID]
493                        }
494                    )
495                    
496                case Type<MetadataViews.ExternalURL>():
497                    return MetadataViews.ExternalURL(self.getCardURL())
498
499                case Type<MetadataViews.NFTCollectionData>():
500                    return BBCollectables.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>())
501                case Type<MetadataViews.NFTCollectionDisplay>():
502                     return BBCollectables.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionDisplay>())
503
504                case Type<MetadataViews.Serial>():
505                    return MetadataViews.Serial(
506                        UInt64(self.data.serialNumber)
507                    )
508
509                case Type<MetadataViews.Traits>():
510                    // sports radar team id
511                    let excludedNames: [String] = ["timestamp"]
512                    // non play specific traits
513                    let traitDictionary: {String: AnyStruct} = {
514                        "packName": BBCollectables.getPackName(packID: self.data.packID)
515                    }
516                    // add play specific data
517                    let fullDictionary = self.mapCardData(dict: traitDictionary)
518                    return MetadataViews.dictToTraits(dict: fullDictionary, excludedNames: excludedNames)
519
520                case Type<MetadataViews.Medias>():
521                    return MetadataViews.Medias(
522                        [
523                            MetadataViews.Media(
524                                file: MetadataViews.HTTPFile(
525                                    url: self.getCardURL()
526                                ),
527                                mediaType: "image/png"
528                            ),
529                            MetadataViews.Media(
530                                file: MetadataViews.HTTPFile(
531                                    url: self.getCardVideoURL()
532                                ),
533                                mediaType: "video/mp4"
534                            )
535                        ]
536                    )
537            }
538
539            return nil
540        }
541 
542        
543    }
544
545    access(all) resource Admin {
546
547        access(all) fun createCard( metadata: {String: String}, packID: UInt32, mintCap: UInt32): UInt32 {
548
549            pre {
550                metadata.containsKey("rarity") == true: "Metadata missing rarity field"
551                metadata.containsKey("name") == true: "Metadata missing name field"
552                metadata.containsKey("description") == true: "Metadata missing description field"
553                metadata.containsKey("date") == true: "Metadata missing date field"
554            }
555
556            for value in BBCollectables.cardDatas.values {
557                if value.metadata["name"] == metadata["name"] {
558                    return 0
559                }
560            }
561
562            var newCard = Card(metadata: metadata)
563
564            let newID = newCard.cardID
565
566            BBCollectables.nextCardID = BBCollectables.nextCardID +1
567
568            BBCollectables.cardDatas[newID] = newCard
569
570            emit CardCreated(cardID: newCard.cardID, metadata: newCard.metadata)
571
572            self.borrowPack(packID: packID).addCard(cardID: newID, mintCap: mintCap)
573
574            return newID
575        }
576
577        access(all) fun editCard( cardID: UInt32, metadata: {String: String}) {
578
579            pre {
580                metadata.containsKey("rarity") == true: "Metadata missing rarity field"
581                metadata.containsKey("name") == true: "Metadata missing name field"
582                metadata.containsKey("description") == true: "Metadata missing description field"
583                metadata.containsKey("date") == true: "Metadata missing date field"
584            }
585
586            for key in BBCollectables.cardDatas.keys {
587                if key == cardID {
588                    continue
589                }
590                if BBCollectables.cardDatas[key]!.metadata["name"] == metadata["name"] {
591                    panic("Card name must be unique")
592                }
593            }
594
595            BBCollectables.cardDatas[cardID]!.updateMetadata(newMetadata: metadata)
596        }
597
598        
599        access(all) fun updateCardMetadata(cardID: UInt32, newMetadata: {String: String}) {
600            let card = BBCollectables.cardDatas[cardID] ?? panic("Card does not exist")
601            card.updateMetadata(newMetadata: newMetadata)
602        }
603
604        access(all) fun finalizeCard(cardID: UInt32) {
605            let card = BBCollectables.cardDatas[cardID] ?? panic("Card does not exist")
606            card.finalizeCard()
607        }
608
609        access(all) fun unfinalizeCard(cardID: UInt32, newMetadata: {String: String}) {
610            let card = BBCollectables.cardDatas[cardID] ?? panic("Card does not exist")
611            card.unfinalizeCard()
612        }
613
614        access(all) fun createPack(name: String): UInt32 {
615
616            var newPack <- create Pack(name: name)
617
618            BBCollectables.nextPackID = BBCollectables.nextPackID +1
619
620            let newID = newPack.packID
621
622            emit PackCreated(packID: newPack.packID)
623
624            BBCollectables.packs[newID] <-! newPack
625
626            return newID
627        }
628
629        access(all) fun borrowPack(packID: UInt32): &Pack {
630            pre {
631                BBCollectables.packs[packID] != nil: "Cannot borrow Pack: The Pack doesn't exist"
632            }
633            
634            return (&BBCollectables.packs[packID] as &Pack?)!
635        }
636
637        access(all) fun createNewAdmin(): @Admin {
638            return <-create Admin()
639        }
640    }
641
642    access(all) resource interface BBNftCollectionPublic : NonFungibleToken.CollectionPublic {
643        access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection})
644        access(all) fun borrowBBNft(id: UInt64): &BBCollectables.NFT? {
645            // If the result isn't nil, the id of the returned reference
646            // should be the same as the argument to the function
647            post {
648                (result == nil) || (result?.id == id): 
649                    "Cannot borrow Card reference: The ID of the returned reference is incorrect"
650            }
651        }
652    }
653
654    access(all) resource Collection: BBNftCollectionPublic, NonFungibleToken.Collection {
655
656        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
657
658        init() {
659            self.ownedNFTs <- {}
660        }
661
662        // Return a list of NFT types that this receiver accepts
663        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
664            let supportedTypes: {Type: Bool} = {}
665            supportedTypes[Type<@BBCollectables.NFT>()] = true
666            return supportedTypes
667        }
668
669        // Return whether or not the given type is accepted by the collection
670        // A collection that can accept any type should just return true by default
671        access(all) view fun isSupportedNFTType(type: Type): Bool {
672            if type == Type<@BBCollectables.NFT>() {
673                return true
674            }
675            return false
676        }
677
678        // Return the amount of NFTs stored in the collection
679        access(all) view fun getLength(): Int {
680            return self.ownedNFTs.length
681        }
682
683        // Create an empty Collection for TopShot NFTs and return it to the caller
684        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
685            return <- BBCollectables.createEmptyCollection(nftType: Type<@BBCollectables.NFT>())
686        }
687
688        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
689
690            let token <- self.ownedNFTs.remove(key: withdrawID) 
691                ?? panic("Cannot withdraw: Card does not exist in the collection")
692
693            emit Withdraw(id: token.id, from: self.owner?.address)
694            
695            return <-token
696        }
697
698        access(NonFungibleToken.Withdraw) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
699            var batchCollection <- create Collection()
700            
701            for id in ids {
702                batchCollection.deposit(token: <-self.withdraw(withdrawID: id))
703            }
704            
705            return <-batchCollection
706        }
707        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
708            
709
710            let token <- token as! @BBCollectables.NFT
711
712            let id = token.id
713
714            let oldToken <- self.ownedNFTs[id] <- token
715
716            if self.owner?.address != nil {
717                emit Deposit(id: id, to: self.owner?.address)
718            }
719
720            destroy oldToken
721        }
722
723        access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection}) {
724
725            let keys = tokens.getIDs()
726
727            for key in keys {
728                self.deposit(token: <-tokens.withdraw(withdrawID: key))
729            }
730
731            destroy tokens
732        }
733
734        access(all) view fun getIDs(): [UInt64] {
735            return self.ownedNFTs.keys
736        }
737
738        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
739            return &self.ownedNFTs[id]
740        }
741
742        access(all) view fun borrowBBNft(id: UInt64): &BBCollectables.NFT? {
743            return self.borrowNFT(id) as! &BBCollectables.NFT?
744        }
745
746        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
747            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
748                // let BBNFT = nft as! &BBCollectables.NFT
749                return nft as &{ViewResolver.Resolver}
750            }
751            return nil
752        }
753    }
754
755    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
756        if nftType != Type<@BBCollectables.NFT>() {
757            panic("NFT type is not supported")
758        }
759        return <-create BBCollectables.Collection()
760    }
761
762    access(all) view fun getAllCards(): {UInt32: BBCollectables.Card} {
763        return BBCollectables.cardDatas
764    }
765
766
767    access(all) view fun getAllPacks(): {UInt32: BBCollectables.PackData} {
768        return BBCollectables.packDatas
769    }
770
771    access(all) view fun getCardMetaData(cardID: UInt32): {String: String}? {
772        return self.cardDatas[cardID]?.metadata
773    }
774
775        access(all) view fun getCard(cardID: UInt32): BBCollectables.Card? {
776        return self.cardDatas[cardID]
777    }
778
779    access(all) view fun getCardMetaDataByField(cardID: UInt32, field: String): String? {
780        if let card = BBCollectables.cardDatas[cardID] {
781            return card.metadata[field]
782        } else {
783            return nil
784        }
785    }
786
787    access(all) fun getPackData(packID: UInt32): QueryPackData? {
788        if BBCollectables.packs[packID] == nil {
789            return nil
790        } else {
791            return QueryPackData(packID: packID)
792        }
793    }
794
795    access(all) view fun getPackName(packID: UInt32): String? {
796        return BBCollectables.packDatas[packID]?.name
797    }
798
799    access(all) fun getPackIDsByName(packName: String): [UInt32]? {
800        var packIDs: [UInt32] = []
801
802        for packData in BBCollectables.packDatas.values {
803            if packName == packData.name {
804
805                packIDs.append(packData.packID)
806            }
807        }
808
809        if packIDs.length == 0 {
810            return nil
811        } else {
812            return packIDs
813        }
814    }
815
816    access(all) view fun getCardsInPack(packID: UInt32): [UInt32]? {
817
818        return BBCollectables.packs[packID]?.cards
819    }
820
821    access(all) view fun isPackLocked(packID: UInt32): Bool? {
822        return BBCollectables.packs[packID]?.locked
823    }
824
825    //------------------------------------------------------------
826    // Contract MetadataViews
827    //------------------------------------------------------------
828    access(all) view fun getContractViews(resourceType: Type?): [Type] {
829        return [Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.Royalties>()]
830    }
831
832    /// Resolve this contract's metadata views
833    access(all) view fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
834        post {
835            result == nil || result!.getType() == viewType: "The returned view must be of the given type or nil"
836        }
837        switch viewType {
838            case Type<MetadataViews.NFTCollectionData>():
839                    return MetadataViews.NFTCollectionData(
840                        storagePath: /storage/BBNFTCollection,
841                        publicPath: /public/BBNFTCollection,
842                        publicCollection: Type<&BBCollectables.Collection>(),
843                        publicLinkedType: Type<&BBCollectables.Collection>(),
844                        createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
845                            return <-BBCollectables.createEmptyCollection(nftType: Type<@BBCollectables.NFT>())
846                        })
847                    )
848            case Type<MetadataViews.NFTCollectionDisplay>():
849                let bannerImage = MetadataViews.Media(
850                    file: MetadataViews.HTTPFile(
851                        url: "https://media.staging.bluebite.tech/placeholder/collectables/banner.png"
852                    ),
853                    mediaType: "image/png"
854                )
855                let squareImage = MetadataViews.Media(
856                    file: MetadataViews.HTTPFile(
857                        url: "https://media.staging.bluebite.tech/placeholder/collectables/square.png"
858                    ),
859                    mediaType: "image/png"
860                )
861
862                return MetadataViews.NFTCollectionDisplay(
863                    name: "Bluebite Collectables Collection".concat(" - ").concat(BBCollectables.OrganiserName),
864                    description: "Viva seus momentos - Feito com 💛 na Bluebite",
865                    externalURL: MetadataViews.ExternalURL("https://media.staging.bluebite.tech/"),
866                    squareImage: squareImage,
867                    bannerImage: bannerImage,
868                    socials: {
869                        "instagram": MetadataViews.ExternalURL("https://www.instagram.com/richmond_fc/")
870                    }
871                )
872        }
873        return nil
874    }
875
876
877    init() {
878
879        // self.network = "testnet"
880
881        self.OrganiserName = "Urubus"
882
883        self.OrganiserStringIdentifier = "urubus"
884
885        self.cardDatas = {}
886        self.packDatas = {}
887        self.packs <- {}
888        self.nextCardID = 1
889        self.nextPackID = 1
890        self.totalSupply = 0
891
892        self.NftCollectionStoragePath = /storage/urubus
893        self.NftCollectionPublicPath = /public/urubus
894        self.AdminStoragePath = /storage/urubusAdmin
895
896        self.account.storage.save<@Collection>(<- create Collection(), to: self.NftCollectionStoragePath)
897
898        let cap = self.account.capabilities.storage.issue<&BBCollectables.Collection>(self.NftCollectionStoragePath)
899        self.account.capabilities.publish(cap, at: self.NftCollectionPublicPath)
900        // self.account.link<&BBCollectables.Collection{NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection}>(self.NftCollectionPublicPath, target: self.NftCollectionStoragePath)
901
902        let adminResource: @BBCollectables.Admin <- create Admin()
903
904        self.account.storage.save<@Admin>(<- adminResource, to: self.AdminStoragePath)
905
906        emit ContractInitialized()
907
908        
909    }
910}
911