Smart Contract
Seussibles
A.321d8fcde05f6e8c.Seussibles
1/**/
2import MetadataViews from 0x1d7e57aa55817448
3import ViewResolver from 0x1d7e57aa55817448
4import NonFungibleToken from 0x1d7e57aa55817448
5import FungibleToken from 0xf233dcee88fe0abe
6import TiblesApp from 0x5cdeb067561defcb
7import TiblesNFT from 0x5cdeb067561defcb
8import TiblesProducer from 0x5cdeb067561defcb
9import CrossVMMetadataViews from 0x1d7e57aa55817448
10import EVM from 0xe467b9dd11fa00df
11
12access(all) contract Seussibles:
13 NonFungibleToken,
14 TiblesApp,
15 TiblesNFT,
16 TiblesProducer
17{
18 access(all) let appId: String
19 access(all) let title: String
20 access(all) let description: String
21 access(all) let ProducerStoragePath: StoragePath
22 access(all) let ProducerPath: PrivatePath
23 access(all) let ContentPath: PublicPath
24 access(all) let contentCapability: Capability
25 access(all) let CollectionStoragePath: StoragePath
26 access(all) let PublicCollectionPath: PublicPath
27
28 access(all) event ContractInitialized()
29 access(all) event Withdraw(id: UInt64, from: Address?)
30 access(all) event Deposit(id: UInt64, to: Address?)
31 access(all) event MinterCreated(minterId: String)
32 access(all) event TibleMinted(minterId: String, mintNumber: UInt32, id: UInt64)
33 access(all) event TibleDestroyed(id: UInt64)
34 access(all) event PackMinterCreated(minterId: String)
35 access(all) event PackMinted(id: UInt64, printedPackId: String)
36
37 access(all) var totalSupply: UInt64
38
39 access(all) resource NFT: NonFungibleToken.NFT, TiblesNFT.INFT {
40 access(all) let id: UInt64
41 access(all) let mintNumber: UInt32
42
43 access(self) let contentCapability: Capability
44 access(self) let contentId: String
45
46 init(id: UInt64, mintNumber: UInt32, contentCapability: Capability, contentId: String) {
47 self.id = id
48 self.mintNumber = mintNumber
49 self.contentId = contentId
50 self.contentCapability = contentCapability
51 }
52
53 access(all) fun metadata(): {String: AnyStruct}? {
54 let content = self.contentCapability.borrow<&{TiblesProducer.IContent}>() ?? panic("Failed to borrow content provider")
55 return content.getMetadata(contentId: self.contentId)
56 }
57
58 access(all) fun displayData(): {String: String} {
59 let metadata = self.metadata() ?? panic("Missing NFT metadata")
60
61 if (metadata.containsKey("pack")) {
62 return {
63 "name": "Seussibles pack",
64 "description": "A Seussibles pack",
65 "imageUrl": "https://app.seussibles.com/collection/"
66 }
67 }
68
69 let set = metadata["set"]! as! &Seussibles.Set
70 let item = metadata["item"]! as! &Seussibles.Item
71 let variant = metadata["variant"]! as! &Seussibles.Variant
72
73 var edition: String = ""
74 var serialInfo: String = ""
75 if let maxCount = variant.maxCount() {
76 edition = "Limited Edition"
77 serialInfo = "LE | "
78 .concat(variant.title())
79 .concat(" #")
80 .concat(self.mintNumber.toString())
81 .concat("/")
82 .concat(maxCount.toString())
83 } else if let batchSize = variant.batchSize() {
84 edition = "Standard Edition"
85 let mintSeries = (self.mintNumber - 1) / batchSize + 1
86 serialInfo = "S".concat(mintSeries.toString())
87 .concat(" | ")
88 .concat(variant.title())
89 .concat(" #")
90 .concat(self.mintNumber.toString())
91 } else {
92 panic("Missing batch size and max count")
93 }
94
95 let description = serialInfo
96 .concat("\n")
97 .concat(edition)
98 .concat("\n")
99 .concat(set.title())
100
101 let imageUrl = item.imageUrl(variantId: variant.id)
102
103 return {
104 "name": item.title(),
105 "description": description,
106 "imageUrl": imageUrl,
107 "edition": edition,
108 "serialInfo": serialInfo
109 }
110 }
111
112 access(all) fun display(): MetadataViews.Display {
113 let nftData = self.displayData()
114
115 return MetadataViews.Display(
116 name: nftData["name"] ?? "",
117 description: nftData["description"] ?? "",
118 thumbnail: MetadataViews.HTTPFile(url: nftData["imageUrl"] ?? "")
119 )
120 }
121
122 access(all) fun editions(): MetadataViews.Editions {
123 let nftData = self.displayData()
124 let metadata = self.metadata() ?? panic("Missing NFT metadata")
125 if (metadata.containsKey("pack")) {
126 return MetadataViews.Editions([MetadataViews.Edition(name: "Seussibles pack", number: UInt64(self.mintNumber), max: nil)])
127 }
128
129 let variant = metadata["variant"]! as! &Seussibles.Variant
130
131 var maxCount: UInt64? = nil
132 if let count = variant.maxCount() {
133 maxCount = UInt64(count)
134 }
135
136 let editionInfo = MetadataViews.Edition(
137 name: nftData["edition"] ?? "",
138 number: UInt64(self.mintNumber),
139 max: maxCount
140 )
141
142 let editionList: [MetadataViews.Edition] = [editionInfo]
143 return MetadataViews.Editions(editionList)
144 }
145
146 access(all) fun serial(): MetadataViews.Serial {
147 return MetadataViews.Serial(UInt64(self.mintNumber))
148 }
149
150 access(all) view fun royalties(): MetadataViews.Royalties {
151 let feeRecCap = getAccount(0x1f590411eaca135f)
152 .capabilities
153 .get<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)
154
155 return MetadataViews.Royalties([
156 MetadataViews.Royalty(
157 receiver: feeRecCap,
158 cut: UFix64(0.025), // 2.5% royalty
159 description: "Seussibles DUC Royalty",
160 )
161 ])
162 }
163
164 access(all) fun externalURL(): MetadataViews.ExternalURL {
165 return MetadataViews.ExternalURL("https://app.seussibles.com/collection/".concat(self.id.toString()))
166 }
167
168 access(all) fun nftCollectionData(): MetadataViews.NFTCollectionData {
169 return MetadataViews.NFTCollectionData(
170 storagePath: Seussibles.CollectionStoragePath,
171 publicPath: Seussibles.PublicCollectionPath,
172 publicCollection: Type<&Seussibles.Collection>(),
173 publicLinkedType: Type<&Seussibles.Collection>(),
174 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
175 return <-Seussibles.createEmptyCollection(nftType: Type<@Seussibles.NFT>())
176 })
177 )
178 }
179
180 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
181 return <-Seussibles.createEmptyCollection(nftType: Type<@Seussibles.NFT>())
182 }
183
184 access(all) fun nftCollectionDisplay(): MetadataViews.NFTCollectionDisplay {
185 let squareMedia = MetadataViews.Media(
186 file: MetadataViews.HTTPFile(url: "https://i.tibles.com/m/seussibles-flow-icon.png"),
187 mediaType: "image/svg+xml"
188 )
189 let bannerMedia = MetadataViews.Media(
190 file: MetadataViews.HTTPFile(url: "https://i.tibles.com/m/seussibles-flow-collection-banner.png"),
191 mediaType: "image/png"
192 )
193
194 let socialsData: {String: String} = {"twitter":"https://twitter.com/seussibles","discord":"https://discord.gg/tibles"}
195 let socials:{String: MetadataViews.ExternalURL } = {}
196 for key in socialsData.keys {
197 socials[key] = MetadataViews.ExternalURL(socialsData[key]!)
198 }
199
200 return MetadataViews.NFTCollectionDisplay(
201 name: "Seussibles! Dr. Seuss Collection by Tibles",
202 description: "An officially licensed Dr. Seuss digital collecting experience by Tibles. Available on iOS and web, Seussibles! is super accessible, engaging, and fun!",
203 externalURL: MetadataViews.ExternalURL("https://seussibles.com"),
204 squareImage: squareMedia,
205 bannerImage: bannerMedia,
206 socials: socials
207 )
208 }
209
210 access(all) view fun traits(): MetadataViews.Traits {
211 let traits : [MetadataViews.Trait] = []
212 return MetadataViews.Traits(traits)
213 }
214
215 access(all) view fun getViews(): [Type] {
216 return [
217 Type<MetadataViews.Display>(),
218 Type<MetadataViews.Royalties>(),
219 Type<MetadataViews.Editions>(),
220 Type<MetadataViews.ExternalURL>(),
221 Type<MetadataViews.NFTCollectionData>(),
222 Type<MetadataViews.NFTCollectionDisplay>(),
223 Type<MetadataViews.Serial>(),
224 Type<MetadataViews.Traits>(),
225 Type<CrossVMMetadataViews.EVMPointer>()
226 ]
227 }
228
229 access(all) fun resolveView(_ view: Type): AnyStruct? {
230 switch view {
231 case Type<MetadataViews.Display>():
232 return self.display()
233 case Type<MetadataViews.Editions>():
234 return self.editions()
235 case Type<MetadataViews.Serial>():
236 return self.serial()
237 case Type<MetadataViews.Royalties>():
238 return self.royalties()
239 case Type<MetadataViews.ExternalURL>():
240 return self.externalURL()
241 case Type<MetadataViews.NFTCollectionData>():
242 return self.nftCollectionData()
243 case Type<MetadataViews.NFTCollectionDisplay>():
244 return self.nftCollectionDisplay()
245 case Type<MetadataViews.Traits>():
246 return self.traits()
247 case Type<CrossVMMetadataViews.EVMPointer>():
248 return Seussibles.resolveContractView(resourceType: nil, viewType: Type<CrossVMMetadataViews.EVMPointer>())
249 default: return nil
250 }
251 }
252 }
253
254 access(all) resource Collection:
255 NonFungibleToken.Collection,
256 TiblesNFT.CollectionPublic
257 {
258 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
259
260 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
261 let supportedTypes: {Type: Bool} = {}
262 supportedTypes[Type<@Seussibles.NFT>()] = true
263 return supportedTypes
264 }
265
266 access(all) view fun isSupportedNFTType(type: Type): Bool {
267 return type == Type<@Seussibles.NFT>()
268 }
269
270 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
271 let tible <- token as! @Seussibles.NFT
272 self.depositTible(tible: <- tible)
273 }
274
275 access(all) fun depositTible(tible: @{TiblesNFT.INFT}) {
276 pre {
277 self.ownedNFTs[tible.id] == nil: "tible with this id already exists"
278 }
279 let token <- tible as! @Seussibles.NFT
280 let id = token.id
281 self.ownedNFTs[id] <-! token
282
283 if self.owner?.address != nil {
284 emit Deposit(id: id, to: self.owner?.address)
285 }
286 }
287
288 access(all) view fun getIDs(): [UInt64] {
289 return self.ownedNFTs.keys
290 }
291
292 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
293 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
294 }
295
296 access(all) view fun borrowTible(id: UInt64): &{TiblesNFT.INFT} {
297 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
298 return nft as! &Seussibles.NFT
299 }
300 panic("Failed to borrow NFT with ID: ".concat(id.toString()))
301 }
302
303 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
304 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Could not withdraw an NFT with the provided ID from the collection")
305 emit Withdraw(id: token.id, from: self.owner?.address)
306 return <-token
307 }
308
309 access(NonFungibleToken.Withdraw) fun withdrawTible(id: UInt64): @Seussibles.NFT {
310 let token <- self.ownedNFTs.remove(key: id) ?? panic("Cannot withdraw: tible does not exist in the collection")
311 let tible <- token as! @Seussibles.NFT
312 emit Withdraw(id: tible.id, from: self.owner?.address)
313 return <-tible
314 }
315
316 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
317 if let token = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
318 return token as &{ViewResolver.Resolver}
319 }
320 return nil
321 }
322
323 access(all) fun tibleDescriptions(): {UInt64: {String: AnyStruct}} {
324 var descriptions: {UInt64: {String: AnyStruct}} = {}
325
326 for id in self.ownedNFTs.keys {
327 let ref = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}?
328 let nft = ref as! &NFT
329 var description: {String: AnyStruct} = {}
330 description["mintNumber"] = nft.mintNumber
331 description["metadata"] = nft.metadata()
332 descriptions[id] = description
333 }
334
335 return descriptions
336 }
337
338 access(NonFungibleToken.Withdraw) fun destroyTible(id: UInt64) {
339 let token <- self.withdraw(withdrawID: id)
340 emit TibleDestroyed(id: id)
341 destroy token
342 }
343
344
345 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
346 return <-Seussibles.createEmptyCollection(nftType: Type<@Seussibles.NFT>())
347 }
348
349 init () {
350 self.ownedNFTs <- {}
351 }
352 }
353
354 access(all) view fun getContractViews(resourceType: Type?): [Type] {
355 return [
356 Type<MetadataViews.NFTCollectionData>(),
357 Type<MetadataViews.NFTCollectionDisplay>(),
358 Type<CrossVMMetadataViews.EVMPointer>()
359 ]
360 }
361
362 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
363 let squareMedia = MetadataViews.Media(
364 file: MetadataViews.HTTPFile(url: "https://i.tibles.com/m/seussibles-flow-icon.png"),
365 mediaType: "image/svg+xml"
366 )
367 let bannerMedia = MetadataViews.Media(
368 file: MetadataViews.HTTPFile(url: "https://i.tibles.com/m/seussibles-flow-collection-banner.png"),
369 mediaType: "image/png"
370 )
371
372 let socialsData: {String: String} = {"twitter":"https://twitter.com/seussibles","discord":"https://discord.gg/tibles"}
373 let socials:{String: MetadataViews.ExternalURL } = {}
374 for key in socialsData.keys {
375 socials[key] = MetadataViews.ExternalURL(socialsData[key]!)
376 }
377
378 switch viewType {
379 case Type<MetadataViews.NFTCollectionData>():
380 let collectionData = MetadataViews.NFTCollectionData(
381 storagePath: self.CollectionStoragePath,
382 publicPath: self.PublicCollectionPath,
383 publicCollection: Type<&Seussibles.Collection>(),
384 publicLinkedType: Type<&Seussibles.Collection>(),
385 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
386 return <-Seussibles.createEmptyCollection(nftType: Type<@Seussibles.NFT>())
387 })
388 )
389 return collectionData
390 case Type<MetadataViews.NFTCollectionDisplay>():
391 let media = MetadataViews.Media(
392 file: MetadataViews.HTTPFile(url: "https://.svg"),
393 mediaType: "image/svg+xml"
394 )
395 return MetadataViews.NFTCollectionDisplay(
396 name: "Seussibles! Dr. Seuss Collection by Tibles",
397 description: "An officially licensed Dr. Seuss digital collecting experience by Tibles. Available on iOS and web, Seussibles! is super accessible, engaging, and fun!",
398 externalURL: MetadataViews.ExternalURL("https://seussibles.com"),
399 squareImage: squareMedia,
400 bannerImage: bannerMedia,
401 socials: socials
402 )
403 case Type<CrossVMMetadataViews.EVMPointer>():
404 return CrossVMMetadataViews.EVMPointer(
405 cadenceType: Type<@Seussibles.NFT>(),
406 cadenceContractAddress: self.account.address,
407 evmContractAddress: EVM.addressFromString("0x3F013CD02C8803217366ee9587FDE1652ac5A57e"),
408 nativeVM: CrossVMMetadataViews.VM.Cadence
409 )
410 }
411 return nil
412 }
413
414 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
415 return <- create Collection()
416 }
417
418 access(all) struct ContentLocation: TiblesProducer.ContentLocation {
419 access(all) let setId: String
420 access(all) let itemId: String
421 access(all) let variantId: String
422
423 init(setId: String, itemId: String, variantId: String) {
424 self.setId = setId
425 self.itemId = itemId
426 self.variantId = variantId
427 }
428 }
429
430 access(all) struct interface IContentLocation {}
431
432 access(all) resource Producer: TiblesProducer.IProducer, TiblesProducer.IContent {
433 access(contract) let minters: @{String: {TiblesProducer.Minter}}
434 access(contract) let contentIdsToPaths: {String: {TiblesProducer.ContentLocation}}
435 access(contract) let sets: {String: Set}
436
437 access(all) fun minter(id: String): &Minter? {
438 let ref = &self.minters[id] as &{TiblesProducer.IMinter}?
439 return ref as! &Minter?
440 }
441
442 access(all) fun set(id: String): &Set? {
443 return &self.sets[id] as &Set?
444 }
445
446 access(all) fun addSet(_ set: Set, contentCapability: Capability) {
447 pre {
448 self.sets[set.id] == nil: "Set with id: ".concat(set.id).concat(" already exists")
449 }
450
451 self.sets[set.id] = set
452
453 for item in set.items.values {
454 for variant in set.variants.values {
455 let limit: UInt32? = variant.maxCount()
456
457 let minterId: String = set.id.concat(":").concat(item.id).concat(":").concat(variant.id)
458 let minter <- create Minter(id: minterId, limit: limit, contentCapability: contentCapability)
459
460 if self.minters.keys.contains(minterId) {
461 panic("Minter ID ".concat(minterId).concat(" already exists."))
462 }
463
464 self.minters[minterId] <-! minter as! @{TiblesProducer.Minter}
465
466 let path = ContentLocation(setId: set.id, itemId: item.id, variantId: variant.id)
467 self.contentIdsToPaths[minterId] = path
468
469 emit MinterCreated(minterId: minterId)
470 }
471 }
472 }
473
474 access(all) fun getMetadata(contentId: String): {String: AnyStruct}? {
475 let path = self.contentIdsToPaths[contentId] ?? panic("Failed to get content path")
476 let location = path as! ContentLocation
477 let set = self.set(id: location.setId) ?? panic("The set does not exist!")
478 let item = set.item(location.itemId) ?? panic("Item metadata is nil")
479 let variant = set.variant(location.variantId) ?? panic("Variant metadata is nil")
480
481 var metadata: {String: AnyStruct} = {}
482 metadata["set"] = set
483 metadata["item"] = item
484 metadata["variant"] = variant
485 return metadata
486 }
487
488 init() {
489 self.sets = {}
490 self.contentIdsToPaths = {}
491 self.minters <- {}
492 }
493 }
494
495 access(all) struct Set {
496 access(all) let id: String
497 access(contract) let items: {String: Item}
498 access(contract) let variants: {String: Variant}
499 access(contract) var metadata: {String: AnyStruct}?
500
501 access(all) fun title(): String {
502 return self.metadata!["title"]! as! String
503 }
504
505 access(all) fun item(_ id: String): &Item? {
506 return &self.items[id] as &Item?
507 }
508
509 access(all) fun variant(_ id: String): &Variant? {
510 return &self.variants[id] as &Variant?
511 }
512
513 access(all) fun update(title: String) {
514 self.metadata = {
515 "title": title
516 }
517 }
518
519 init(id: String, title: String, items: {String: Item}, variants: {String: Variant}) {
520 self.id = id
521 self.items = items
522 self.variants = variants
523 self.metadata = nil
524 self.update(title: title)
525 }
526 }
527
528 access(all) struct Item {
529 access(all) let id: String
530 access(contract) var metadata: {String: AnyStruct}?
531
532 access(all) fun title(): String {
533 return self.metadata!["title"]! as! String
534 }
535
536 access(all) fun imageUrl(variantId: String): String {
537 let imageUrls = self.metadata!["imageUrls"]! as! {String: String}
538 return imageUrls[variantId]!
539 }
540
541 access(all) fun update(title: String, imageUrls: {String: String}) {
542 self.metadata = {
543 "title": title,
544 "imageUrls": imageUrls
545 }
546 }
547
548 init(id: String, title: String, imageUrls: {String: String}) {
549 self.id = id
550 self.metadata = nil
551 self.update(title: title, imageUrls: imageUrls)
552 }
553 }
554
555 access(all) struct Variant {
556 access(all) let id: String
557 access(contract) var metadata: {String: AnyStruct}?
558
559 access(all) fun title(): String {
560 return self.metadata!["title"]! as! String
561 }
562
563 access(all) fun batchSize(): UInt32? {
564 return self.metadata!["batchSize"] as! UInt32?
565 }
566
567 access(all) fun maxCount(): UInt32? {
568 return self.metadata!["maxCount"] as! UInt32?
569 }
570
571 access(all) fun update(title: String, batchSize: UInt32?, maxCount: UInt32?) {
572 assert((batchSize == nil) != (maxCount == nil), message: "batch size or max count can be used, not both")
573 let metadata: {String: AnyStruct} = {
574 "title": title
575 }
576 let previousBatchSize = (self.metadata ?? {})["batchSize"] as! UInt32?
577 let previousMaxCount = (self.metadata ?? {})["maxCount"] as! UInt32?
578 if let batchSize = batchSize {
579 assert(previousMaxCount == nil, message: "Cannot change from max count to batch size")
580 assert(previousBatchSize == nil || previousBatchSize == batchSize, message: "batch size cannot be changed once set")
581 metadata["batchSize"] = batchSize
582 }
583 if let maxCount = maxCount {
584 assert(previousBatchSize == nil, message: "Cannot change from batch size to max count")
585 assert(previousMaxCount == nil || previousMaxCount == maxCount, message: "max count cannot be changed once set")
586 metadata["maxCount"] = maxCount
587 }
588 self.metadata = metadata
589 }
590
591 init(id: String, title: String, batchSize: UInt32?, maxCount: UInt32?) {
592 self.id = id
593 self.metadata = nil
594 self.update(title: title, batchSize: batchSize, maxCount: maxCount)
595 }
596 }
597
598 access(all) resource Minter: TiblesProducer.Minter {
599 access(all) let id: String
600 access(all) var lastMintNumber: UInt32
601 access(contract) let tibles: @{UInt32: {TiblesNFT.INFT}}
602 access(all) let limit: UInt32?
603 access(all) let contentCapability: Capability
604
605 access(all) fun withdraw(mintNumber: UInt32): @{TiblesNFT.INFT} {
606 pre {
607 self.tibles[mintNumber] != nil: "The tible does not exist in this minter."
608 }
609 return <- self.tibles.remove(key: mintNumber)!
610 }
611
612 access(all) fun mintNext() {
613 if let limit = self.limit {
614 if self.lastMintNumber >= limit {
615 panic("You've hit the limit for number of tokens in this minter!")
616 }
617 }
618
619 let id = Seussibles.totalSupply + 1
620 let mintNumber = self.lastMintNumber + 1
621 let tible <- create NFT(id: id, mintNumber: mintNumber, contentCapability: self.contentCapability, contentId: self.id)
622 self.tibles[mintNumber] <-! tible
623 self.lastMintNumber = mintNumber
624 Seussibles.totalSupply = id
625
626 emit TibleMinted(minterId: self.id, mintNumber: mintNumber, id: id)
627 }
628
629 init(id: String, limit: UInt32?, contentCapability: Capability) {
630 self.id = id
631 self.lastMintNumber = 0
632 self.tibles <- {}
633 self.limit = limit
634 self.contentCapability = contentCapability
635 }
636 }
637
638 access(all) resource PackMinter: TiblesProducer.IContent{
639 access(all) let id: String
640 access(all) var lastMintNumber: UInt32
641 access(contract) let packs: @{UInt64: {TiblesNFT.INFT}}
642 access(contract) let contentIdsToPaths: {String: {TiblesProducer.ContentLocation}}
643 access(all) let contentCapability: Capability
644
645 access(all) fun getMetadata(contentId: String): {String: AnyStruct}? {
646 return {
647 "pack": "Seussibles"
648 }
649 }
650
651 access(all) fun withdraw(id: UInt64): @{TiblesNFT.INFT} {
652 pre {
653 self.packs[id] != nil: "The pack does not exist in this minter."
654 }
655 return <- self.packs.remove(key: id)!
656 }
657
658 access(all) fun mintNext(printedPackId: String) {
659 let id = Seussibles.totalSupply + 1
660 let mintNumber = self.lastMintNumber + 1
661 let pack <- create NFT(id: id, mintNumber: mintNumber, contentCapability: self.contentCapability, contentId: self.id)
662 self.packs[id] <-! pack
663 self.lastMintNumber = mintNumber
664 Seussibles.totalSupply = id
665 emit PackMinted(id: id, printedPackId: printedPackId)
666 }
667
668 init(id: String, contentCapability: Capability) {
669 self.id = id
670 self.lastMintNumber = 0
671 self.packs <- {}
672 self.contentCapability = contentCapability
673 self.contentIdsToPaths = {}
674 emit PackMinterCreated(minterId: self.id)
675 }
676 }
677
678 access(all) fun createNewPackMinter(id: String, contentCapability: Capability): @PackMinter {
679 assert(self.account.address == 0x321d8fcde05f6e8c, message: "wrong address")
680 return <- create PackMinter(id: id, contentCapability: contentCapability)
681 }
682
683 init() {
684 self.totalSupply = 0
685
686 self.appId = "com.tibles.seussibles"
687 self.title = "Seussibles"
688 self.description = "Dr. Seuss officially licensed digital collectibles"
689
690 self.ProducerStoragePath = /storage/TiblesSeussiblesProducer
691 self.ProducerPath = /private/TiblesSeussiblesProducer
692 self.ContentPath = /public/TiblesSeussiblesContent
693 self.CollectionStoragePath = /storage/TiblesSeussiblesCollection
694 self.PublicCollectionPath = /public/TiblesSeussiblesCollection
695
696 let producer <- create Producer()
697 self.account.storage.save(<-producer, to: self.ProducerStoragePath)
698
699 let cap = self.account.capabilities.storage.issue<&Producer>(self.ProducerStoragePath)
700 self.account.capabilities.publish(cap, at: self.ContentPath)
701 self.contentCapability = self.account.capabilities.get<&Producer>(self.ContentPath)
702
703 emit ContractInitialized()
704 }
705}
706