Smart Contract
OneShots
A.4f7ff543c936072b.OneShots
1/*
2The following terms apply to purchasers of NFTs that contain Valiant Entertainment, LLC (“Valiant”)
3content. As between you and Valiant you agree that all rights, title and interest (including all
4copyright, trademark, service marks, and any and all intellectual property rights of any kind,
5whether registered or unregistered) to the artworks, images, videos, animations, design, drawings,
6logos and/or other digital content (“Digital Assets”) featured on and/or within this NFT are the
7sole and exclusive property of Valiant.
8
9Subject to your rightful and lawful purchase of this NFT, Valiant grants you a personal, non-
10commercial, non-exclusive, non-transferable, non-sublicensable, revocable, limited license to
11download, view, and display the Digital Assets on and/or within this NFT. As such, you agree that
12you shall not: (i) modify, distort or perform any other change to the Digital Assets in any way,
13(ii) use the Digital Assets as a brand or trademark or to advertise, market, or sell any third
14party product or service; (iii) use the Digital Assets in connection with images, videos, or other
15forms of media that depict hatred, intolerance, violence, cruelty, or anything else that could
16reasonably be found to constitute hate speech or otherwise infringe upon the rights of others or
17promote illegal activities, or reflect negatively on Valiant; (iv) use the Digital Assets in movies,
18videos, or any other forms of media, except solely for your own personal, non-commercial use;
19(v) sell, distribute for commercial gain (including, without limitation, giving away in the hopes
20of eventual commercial gain), or otherwise commercialize merchandise that includes, contains, or
21consists of the Digital Assets; (vi) attempt to trademark, copyright, or otherwise acquire additional
22intellectual property rights in or to the Digital Assets; (vii) use the Digital Assets in connection
23with defamatory or dishonest statements about Valiant and/or its affiliated companies or which
24otherwise damage the goodwill, value or reputation of Valiant or represent or imply that your
25exercise of the license granted to you herein is endorsed by Valiant and/or our affiliated companies;
26or (viii) otherwise utilize the Digital Assets for your or any third party’s commercial benefit.
27Notwithstanding the above prohibitions, these restrictions do not impede your ability to re-sell,
28donate and/or transfer ownership of this NFT through the OneShots mobile application and/or website
29and/or through secondary NFT marketplaces (e.g. OpenSea).
30
31For the avoidance of doubt, the limited license granted herein applies only to the extent that you
32continue to own the NFT. If at any time you re-sell, transfer or donate this NFT to another party,
33your rights under this limited licensed will immediately be revoked and you will have no further
34rights in and/or to the Digital Assets.
35*/
36import MetadataViews from 0x1d7e57aa55817448
37import ViewResolver from 0x1d7e57aa55817448
38import NonFungibleToken from 0x1d7e57aa55817448
39import FungibleToken from 0xf233dcee88fe0abe
40import TiblesApp from 0x5cdeb067561defcb
41import TiblesNFT from 0x5cdeb067561defcb
42import TiblesProducer from 0x5cdeb067561defcb
43import CrossVMMetadataViews from 0x1d7e57aa55817448
44import EVM from 0xe467b9dd11fa00df
45
46access(all) contract OneShots:
47 NonFungibleToken,
48 TiblesApp,
49 TiblesNFT,
50 TiblesProducer
51{
52 access(all) let appId: String
53 access(all) let title: String
54 access(all) let description: String
55 access(all) let ProducerStoragePath: StoragePath
56 access(all) let ProducerPath: PrivatePath
57 access(all) let ContentPath: PublicPath
58 access(all) let contentCapability: Capability
59 access(all) let CollectionStoragePath: StoragePath
60 access(all) let PublicCollectionPath: PublicPath
61
62 access(all) event ContractInitialized()
63 access(all) event Withdraw(id: UInt64, from: Address?)
64 access(all) event Deposit(id: UInt64, to: Address?)
65 access(all) event MinterCreated(minterId: String)
66 access(all) event TibleMinted(minterId: String, mintNumber: UInt32, id: UInt64)
67 access(all) event TibleDestroyed(id: UInt64)
68 access(all) event PackMinterCreated(minterId: String)
69 access(all) event PackMinted(id: UInt64, printedPackId: String)
70
71 access(all) var totalSupply: UInt64
72
73 access(all) resource NFT: NonFungibleToken.NFT, TiblesNFT.INFT {
74 access(all) let id: UInt64
75 access(all) let mintNumber: UInt32
76
77 access(self) let contentCapability: Capability
78 access(self) let contentId: String
79
80 init(id: UInt64, mintNumber: UInt32, contentCapability: Capability, contentId: String) {
81 self.id = id
82 self.mintNumber = mintNumber
83 self.contentId = contentId
84 self.contentCapability = contentCapability
85 }
86
87 access(all) fun metadata(): {String: AnyStruct}? {
88 let content = self.contentCapability.borrow<&{TiblesProducer.IContent}>() ?? panic("Failed to borrow content provider")
89 return content.getMetadata(contentId: self.contentId)
90 }
91
92 access(all) fun displayData(): {String: String} {
93 let metadata = self.metadata() ?? panic("Missing NFT metadata")
94
95 if (metadata.containsKey("pack")) {
96 return {
97 "name": "OneShots pack",
98 "description": "A OneShots pack",
99 "imageUrl": "https://i.tibles.com/m/oneshots-flow-icon.png"
100 }
101 }
102
103 let set = metadata["set"]! as! &OneShots.Set
104 let item = metadata["item"]! as! &OneShots.Item
105 let variant = metadata["variant"]! as! &OneShots.Variant
106
107 var edition: String = ""
108 var serialInfo: String = ""
109 if let maxCount = variant.maxCount() {
110 edition = "Limited Edition"
111 serialInfo = "LE | "
112 .concat(variant.title())
113 .concat(" #")
114 .concat(self.mintNumber.toString())
115 .concat("/")
116 .concat(maxCount.toString())
117 } else if let batchSize = variant.batchSize() {
118 edition = "Standard Edition"
119 let mintSeries = (self.mintNumber - 1) / batchSize + 1
120 serialInfo = "S".concat(mintSeries.toString())
121 .concat(" | ")
122 .concat(variant.title())
123 .concat(" #")
124 .concat(self.mintNumber.toString())
125 } else {
126 panic("Missing batch size and max count")
127 }
128
129 let description = serialInfo
130 .concat("\n")
131 .concat(edition)
132 .concat("\n")
133 .concat(set.title())
134
135 let imageUrl = item.imageUrl(variantId: variant.id)
136
137 return {
138 "name": item.title(),
139 "description": description,
140 "imageUrl": imageUrl,
141 "edition": edition,
142 "serialInfo": serialInfo
143 }
144 }
145
146 access(all) fun display(): MetadataViews.Display {
147 let nftData = self.displayData()
148
149 return MetadataViews.Display(
150 name: nftData["name"] ?? "",
151 description: nftData["description"] ?? "",
152 thumbnail: MetadataViews.HTTPFile(url: nftData["imageUrl"] ?? "")
153 )
154 }
155
156 access(all) fun editions(): MetadataViews.Editions {
157 let nftData = self.displayData()
158 let metadata = self.metadata() ?? panic("Missing NFT metadata")
159 if (metadata.containsKey("pack")) {
160 return MetadataViews.Editions([MetadataViews.Edition(name: "OneShots pack", number: UInt64(self.mintNumber), max: nil)])
161 }
162
163 let variant = metadata["variant"]! as! &OneShots.Variant
164
165 var maxCount: UInt64? = nil
166 if let count = variant.maxCount() {
167 maxCount = UInt64(count)
168 }
169
170 let editionInfo = MetadataViews.Edition(
171 name: nftData["edition"] ?? "",
172 number: UInt64(self.mintNumber),
173 max: maxCount
174 )
175
176 let editionList: [MetadataViews.Edition] = [editionInfo]
177 return MetadataViews.Editions(editionList)
178 }
179
180 access(all) fun serial(): MetadataViews.Serial {
181 return MetadataViews.Serial(UInt64(self.mintNumber))
182 }
183
184 access(all) view fun royalties(): MetadataViews.Royalties {
185 let feeRecCap = getAccount(0x1f590411eaca135f)
186 .capabilities
187 .get<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)
188
189 return MetadataViews.Royalties([
190 MetadataViews.Royalty(
191 receiver: feeRecCap,
192 cut: UFix64(0.025), // 2.5% royalty
193 description: "OneShots DUC Royalty",
194 )
195 ])
196 }
197
198 access(all) fun externalURL(): MetadataViews.ExternalURL {
199 return MetadataViews.ExternalURL("https://app.oneshots.com/collection".concat(self.id.toString()))
200 }
201
202 access(all) fun nftCollectionData(): MetadataViews.NFTCollectionData {
203 return MetadataViews.NFTCollectionData(
204 storagePath: OneShots.CollectionStoragePath,
205 publicPath: OneShots.PublicCollectionPath,
206 publicCollection: Type<&OneShots.Collection>(),
207 publicLinkedType: Type<&OneShots.Collection>(),
208 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
209 return <-OneShots.createEmptyCollection(nftType: Type<@OneShots.NFT>())
210 })
211 )
212 }
213
214 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
215 return <-OneShots.createEmptyCollection(nftType: Type<@OneShots.NFT>())
216 }
217
218 access(all) fun nftCollectionDisplay(): MetadataViews.NFTCollectionDisplay {
219 let squareMedia = MetadataViews.Media(
220 file: MetadataViews.HTTPFile(url: "https://i.tibles.com/m/oneshots-flow-icon.png"),
221 mediaType: "image/svg+xml"
222 )
223 let bannerMedia = MetadataViews.Media(
224 file: MetadataViews.HTTPFile(url: "https://i.tibles.com/m/oneshots-flow-collection-banner.png"),
225 mediaType: "image/png"
226 )
227
228 let socialsData: {String: String} = {"twitter": "https://twitter.com/oneshotstibles"}
229 let socials:{String: MetadataViews.ExternalURL } = {}
230 for key in socialsData.keys {
231 socials[key] = MetadataViews.ExternalURL(socialsData[key]!)
232 }
233
234 return MetadataViews.NFTCollectionDisplay(
235 name: "OneShots Comic Book Trading Cards by Tibles",
236 description: "OneShots is a digital trading card collecting experience by Tibles, made just for comic book fans, backed by the FLOW blockchain.",
237 externalURL: MetadataViews.ExternalURL("https://app.oneshots.com"),
238 squareImage: squareMedia,
239 bannerImage: bannerMedia,
240 socials: socials
241 )
242 }
243
244 access(all) view fun traits(): MetadataViews.Traits {
245 let traits : [MetadataViews.Trait] = []
246 return MetadataViews.Traits(traits)
247 }
248
249 access(all) view fun getViews(): [Type] {
250 return [
251 Type<MetadataViews.Display>(),
252 Type<MetadataViews.Royalties>(),
253 Type<MetadataViews.Editions>(),
254 Type<MetadataViews.ExternalURL>(),
255 Type<MetadataViews.NFTCollectionData>(),
256 Type<MetadataViews.NFTCollectionDisplay>(),
257 Type<MetadataViews.Serial>(),
258 Type<MetadataViews.Traits>(),
259 Type<CrossVMMetadataViews.EVMPointer>()
260 ]
261 }
262
263 access(all) fun resolveView(_ view: Type): AnyStruct? {
264 switch view {
265 case Type<MetadataViews.Display>():
266 return self.display()
267 case Type<MetadataViews.Editions>():
268 return self.editions()
269 case Type<MetadataViews.Serial>():
270 return self.serial()
271 case Type<MetadataViews.Royalties>():
272 return self.royalties()
273 case Type<MetadataViews.ExternalURL>():
274 return self.externalURL()
275 case Type<MetadataViews.NFTCollectionData>():
276 return self.nftCollectionData()
277 case Type<MetadataViews.NFTCollectionDisplay>():
278 return self.nftCollectionDisplay()
279 case Type<MetadataViews.Traits>():
280 return self.traits()
281 case Type<CrossVMMetadataViews.EVMPointer>():
282 return OneShots.resolveContractView(resourceType: nil, viewType: Type<CrossVMMetadataViews.EVMPointer>())
283 default: return nil
284 }
285 }
286 }
287
288 access(all) resource Collection:
289 NonFungibleToken.Collection,
290 TiblesNFT.CollectionPublic
291 {
292 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
293
294 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
295 let supportedTypes: {Type: Bool} = {}
296 supportedTypes[Type<@OneShots.NFT>()] = true
297 return supportedTypes
298 }
299
300 access(all) view fun isSupportedNFTType(type: Type): Bool {
301 return type == Type<@OneShots.NFT>()
302 }
303
304 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
305 let tible <- token as! @OneShots.NFT
306 self.depositTible(tible: <- tible)
307 }
308
309 access(all) fun depositTible(tible: @{TiblesNFT.INFT}) {
310 pre {
311 self.ownedNFTs[tible.id] == nil: "tible with this id already exists"
312 }
313 let token <- tible as! @OneShots.NFT
314 let id = token.id
315 self.ownedNFTs[id] <-! token
316
317 if self.owner?.address != nil {
318 emit Deposit(id: id, to: self.owner?.address)
319 }
320 }
321
322 access(all) view fun getIDs(): [UInt64] {
323 return self.ownedNFTs.keys
324 }
325
326 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
327 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
328 }
329
330 access(all) view fun borrowTible(id: UInt64): &{TiblesNFT.INFT} {
331 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
332 return nft as! &OneShots.NFT
333 }
334 panic("Failed to borrow NFT with ID: ".concat(id.toString()))
335 }
336
337 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
338 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Could not withdraw an NFT with the provided ID from the collection")
339 emit Withdraw(id: token.id, from: self.owner?.address)
340 return <-token
341 }
342
343 access(NonFungibleToken.Withdraw) fun withdrawTible(id: UInt64): @OneShots.NFT {
344 let token <- self.ownedNFTs.remove(key: id) ?? panic("Cannot withdraw: tible does not exist in the collection")
345 let tible <- token as! @OneShots.NFT
346 emit Withdraw(id: tible.id, from: self.owner?.address)
347 return <-tible
348 }
349
350 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
351 if let token = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
352 return token as &{ViewResolver.Resolver}
353 }
354 return nil
355 }
356
357 access(all) fun tibleDescriptions(): {UInt64: {String: AnyStruct}} {
358 var descriptions: {UInt64: {String: AnyStruct}} = {}
359
360 for id in self.ownedNFTs.keys {
361 let ref = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}?
362 let nft = ref as! &NFT
363 var description: {String: AnyStruct} = {}
364 description["mintNumber"] = nft.mintNumber
365 description["metadata"] = nft.metadata()
366 descriptions[id] = description
367 }
368
369 return descriptions
370 }
371
372 access(NonFungibleToken.Withdraw) fun destroyTible(id: UInt64) {
373 let token <- self.withdraw(withdrawID: id)
374 emit TibleDestroyed(id: id)
375 destroy token
376 }
377
378
379 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
380 return <-OneShots.createEmptyCollection(nftType: Type<@OneShots.NFT>())
381 }
382
383 init () {
384 self.ownedNFTs <- {}
385 }
386 }
387
388 access(all) view fun getContractViews(resourceType: Type?): [Type] {
389 return [
390 Type<MetadataViews.NFTCollectionData>(),
391 Type<MetadataViews.NFTCollectionDisplay>(),
392 Type<CrossVMMetadataViews.EVMPointer>()
393 ]
394 }
395
396 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
397 let squareMedia = MetadataViews.Media(
398 file: MetadataViews.HTTPFile(url: "https://i.tibles.com/m/oneshots-flow-icon.png"),
399 mediaType: "image/svg+xml"
400 )
401 let bannerMedia = MetadataViews.Media(
402 file: MetadataViews.HTTPFile(url: "https://i.tibles.com/m/oneshots-flow-collection-banner.png"),
403 mediaType: "image/png"
404 )
405
406 let socialsData: {String: String} = {"twitter": "https://twitter.com/oneshotstibles"}
407 let socials:{String: MetadataViews.ExternalURL } = {}
408 for key in socialsData.keys {
409 socials[key] = MetadataViews.ExternalURL(socialsData[key]!)
410 }
411
412 switch viewType {
413 case Type<MetadataViews.NFTCollectionData>():
414 let collectionData = MetadataViews.NFTCollectionData(
415 storagePath: self.CollectionStoragePath,
416 publicPath: self.PublicCollectionPath,
417 publicCollection: Type<&OneShots.Collection>(),
418 publicLinkedType: Type<&OneShots.Collection>(),
419 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
420 return <-OneShots.createEmptyCollection(nftType: Type<@OneShots.NFT>())
421 })
422 )
423 return collectionData
424 case Type<MetadataViews.NFTCollectionDisplay>():
425 let media = MetadataViews.Media(
426 file: MetadataViews.HTTPFile(url: "https://.svg"),
427 mediaType: "image/svg+xml"
428 )
429 return MetadataViews.NFTCollectionDisplay(
430 name: "OneShots Comic Book Trading Cards by Tibles",
431 description: "OneShots is a digital trading card collecting experience by Tibles, made just for comic book fans, backed by the FLOW blockchain.",
432 externalURL: MetadataViews.ExternalURL("https://app.oneshots.com"),
433 squareImage: squareMedia,
434 bannerImage: bannerMedia,
435 socials: socials
436 )
437 case Type<CrossVMMetadataViews.EVMPointer>():
438 return CrossVMMetadataViews.EVMPointer(
439 cadenceType: Type<@OneShots.NFT>(),
440 cadenceContractAddress: self.account.address,
441 evmContractAddress: EVM.addressFromString("0xb266949A9d976d0e52cC746C2676C103757C8e11"),
442 nativeVM: CrossVMMetadataViews.VM.Cadence
443 )
444 }
445 return nil
446 }
447
448 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
449 return <- create Collection()
450 }
451
452 access(all) struct ContentLocation: TiblesProducer.ContentLocation {
453 access(all) let setId: String
454 access(all) let itemId: String
455 access(all) let variantId: String
456
457 init(setId: String, itemId: String, variantId: String) {
458 self.setId = setId
459 self.itemId = itemId
460 self.variantId = variantId
461 }
462 }
463
464 access(all) struct interface IContentLocation {}
465
466 access(all) resource Producer: TiblesProducer.IProducer, TiblesProducer.IContent {
467 access(contract) let minters: @{String: {TiblesProducer.Minter}}
468 access(contract) let contentIdsToPaths: {String: {TiblesProducer.ContentLocation}}
469 access(contract) let sets: {String: Set}
470
471 access(all) fun minter(id: String): &Minter? {
472 let ref = &self.minters[id] as &{TiblesProducer.IMinter}?
473 return ref as! &Minter?
474 }
475
476 access(all) fun set(id: String): &Set? {
477 return &self.sets[id] as &Set?
478 }
479
480 access(all) fun addSet(_ set: Set, contentCapability: Capability) {
481 pre {
482 self.sets[set.id] == nil: "Set with id: ".concat(set.id).concat(" already exists")
483 }
484
485 self.sets[set.id] = set
486
487 for item in set.items.values {
488 for variant in set.variants.values {
489 let limit: UInt32? = variant.maxCount()
490
491 let minterId: String = set.id.concat(":").concat(item.id).concat(":").concat(variant.id)
492 let minter <- create Minter(id: minterId, limit: limit, contentCapability: contentCapability)
493
494 if self.minters.keys.contains(minterId) {
495 panic("Minter ID ".concat(minterId).concat(" already exists."))
496 }
497
498 self.minters[minterId] <-! minter as! @{TiblesProducer.Minter}
499
500 let path = ContentLocation(setId: set.id, itemId: item.id, variantId: variant.id)
501 self.contentIdsToPaths[minterId] = path
502
503 emit MinterCreated(minterId: minterId)
504 }
505 }
506 }
507
508 access(all) fun getMetadata(contentId: String): {String: AnyStruct}? {
509 let path = self.contentIdsToPaths[contentId] ?? panic("Failed to get content path")
510 let location = path as! ContentLocation
511 let set = self.set(id: location.setId) ?? panic("The set does not exist!")
512 let item = set.item(location.itemId) ?? panic("Item metadata is nil")
513 let variant = set.variant(location.variantId) ?? panic("Variant metadata is nil")
514
515 var metadata: {String: AnyStruct} = {}
516 metadata["set"] = set
517 metadata["item"] = item
518 metadata["variant"] = variant
519 return metadata
520 }
521
522 init() {
523 self.sets = {}
524 self.contentIdsToPaths = {}
525 self.minters <- {}
526 }
527 }
528
529 access(all) struct Set {
530 access(all) let id: String
531 access(contract) let items: {String: Item}
532 access(contract) let variants: {String: Variant}
533 access(contract) var metadata: {String: AnyStruct}?
534
535 access(all) fun title(): String {
536 return self.metadata!["title"]! as! String
537 }
538
539 access(all) fun item(_ id: String): &Item? {
540 return &self.items[id] as &Item?
541 }
542
543 access(all) fun variant(_ id: String): &Variant? {
544 return &self.variants[id] as &Variant?
545 }
546
547 access(all) fun update(title: String) {
548 self.metadata = {
549 "title": title
550 }
551 }
552
553 init(id: String, title: String, items: {String: Item}, variants: {String: Variant}) {
554 self.id = id
555 self.items = items
556 self.variants = variants
557 self.metadata = nil
558 self.update(title: title)
559 }
560 }
561
562 access(all) struct Item {
563 access(all) let id: String
564 access(contract) var metadata: {String: AnyStruct}?
565
566 access(all) fun title(): String {
567 return self.metadata!["title"]! as! String
568 }
569
570 access(all) fun imageUrl(variantId: String): String {
571 let imageUrls = self.metadata!["imageUrls"]! as! {String: String}
572 return imageUrls[variantId]!
573 }
574
575 access(all) fun update(title: String, imageUrls: {String: String}) {
576 self.metadata = {
577 "title": title,
578 "imageUrls": imageUrls
579 }
580 }
581
582 init(id: String, title: String, imageUrls: {String: String}) {
583 self.id = id
584 self.metadata = nil
585 self.update(title: title, imageUrls: imageUrls)
586 }
587 }
588
589 access(all) struct Variant {
590 access(all) let id: String
591 access(contract) var metadata: {String: AnyStruct}?
592
593 access(all) fun title(): String {
594 return self.metadata!["title"]! as! String
595 }
596
597 access(all) fun batchSize(): UInt32? {
598 return self.metadata!["batchSize"] as! UInt32?
599 }
600
601 access(all) fun maxCount(): UInt32? {
602 return self.metadata!["maxCount"] as! UInt32?
603 }
604
605 access(all) fun update(title: String, batchSize: UInt32?, maxCount: UInt32?) {
606 assert((batchSize == nil) != (maxCount == nil), message: "batch size or max count can be used, not both")
607 let metadata: {String: AnyStruct} = {
608 "title": title
609 }
610 let previousBatchSize = (self.metadata ?? {})["batchSize"] as! UInt32?
611 let previousMaxCount = (self.metadata ?? {})["maxCount"] as! UInt32?
612 if let batchSize = batchSize {
613 assert(previousMaxCount == nil, message: "Cannot change from max count to batch size")
614 assert(previousBatchSize == nil || previousBatchSize == batchSize, message: "batch size cannot be changed once set")
615 metadata["batchSize"] = batchSize
616 }
617 if let maxCount = maxCount {
618 assert(previousBatchSize == nil, message: "Cannot change from batch size to max count")
619 assert(previousMaxCount == nil || previousMaxCount == maxCount, message: "max count cannot be changed once set")
620 metadata["maxCount"] = maxCount
621 }
622 self.metadata = metadata
623 }
624
625 init(id: String, title: String, batchSize: UInt32?, maxCount: UInt32?) {
626 self.id = id
627 self.metadata = nil
628 self.update(title: title, batchSize: batchSize, maxCount: maxCount)
629 }
630 }
631
632 access(all) resource Minter: TiblesProducer.Minter {
633 access(all) let id: String
634 access(all) var lastMintNumber: UInt32
635 access(contract) let tibles: @{UInt32: {TiblesNFT.INFT}}
636 access(all) let limit: UInt32?
637 access(all) let contentCapability: Capability
638
639 access(all) fun withdraw(mintNumber: UInt32): @{TiblesNFT.INFT} {
640 pre {
641 self.tibles[mintNumber] != nil: "The tible does not exist in this minter."
642 }
643 return <- self.tibles.remove(key: mintNumber)!
644 }
645
646 access(all) fun mintNext() {
647 if let limit = self.limit {
648 if self.lastMintNumber >= limit {
649 panic("You've hit the limit for number of tokens in this minter!")
650 }
651 }
652
653 let id = OneShots.totalSupply + 1
654 let mintNumber = self.lastMintNumber + 1
655 let tible <- create NFT(id: id, mintNumber: mintNumber, contentCapability: self.contentCapability, contentId: self.id)
656 self.tibles[mintNumber] <-! tible
657 self.lastMintNumber = mintNumber
658 OneShots.totalSupply = id
659
660 emit TibleMinted(minterId: self.id, mintNumber: mintNumber, id: id)
661 }
662
663 init(id: String, limit: UInt32?, contentCapability: Capability) {
664 self.id = id
665 self.lastMintNumber = 0
666 self.tibles <- {}
667 self.limit = limit
668 self.contentCapability = contentCapability
669 }
670 }
671
672 access(all) resource PackMinter: TiblesProducer.IContent{
673 access(all) let id: String
674 access(all) var lastMintNumber: UInt32
675 access(contract) let packs: @{UInt64: {TiblesNFT.INFT}}
676 access(contract) let contentIdsToPaths: {String: {TiblesProducer.ContentLocation}}
677 access(all) let contentCapability: Capability
678
679 access(all) fun getMetadata(contentId: String): {String: AnyStruct}? {
680 return {
681 "pack": "OneShots"
682 }
683 }
684
685 access(all) fun withdraw(id: UInt64): @{TiblesNFT.INFT} {
686 pre {
687 self.packs[id] != nil: "The pack does not exist in this minter."
688 }
689 return <- self.packs.remove(key: id)!
690 }
691
692 access(all) fun mintNext(printedPackId: String) {
693 let id = OneShots.totalSupply + 1
694 let mintNumber = self.lastMintNumber + 1
695 let pack <- create NFT(id: id, mintNumber: mintNumber, contentCapability: self.contentCapability, contentId: self.id)
696 self.packs[id] <-! pack
697 self.lastMintNumber = mintNumber
698 OneShots.totalSupply = id
699 emit PackMinted(id: id, printedPackId: printedPackId)
700 }
701
702 init(id: String, contentCapability: Capability) {
703 self.id = id
704 self.lastMintNumber = 0
705 self.packs <- {}
706 self.contentCapability = contentCapability
707 self.contentIdsToPaths = {}
708 emit PackMinterCreated(minterId: self.id)
709 }
710 }
711
712 access(all) fun createNewPackMinter(id: String, contentCapability: Capability): @PackMinter {
713 assert(self.account.address == 0x4f7ff543c936072b, message: "wrong address")
714 return <- create PackMinter(id: id, contentCapability: contentCapability)
715 }
716
717 init() {
718 self.totalSupply = 0
719
720 self.appId = "com.tibles.oneshots"
721 self.title = "One Shots"
722 self.description = "OneShots is a digital trading card collecting experience by Tibles, made just for comic book fans, backed by the FLOW blockchain."
723
724 self.ProducerStoragePath = /storage/TiblesOneShotsProducer
725 self.ProducerPath = /private/TiblesOneShotsProducer
726 self.ContentPath = /public/TiblesOneShotsContent
727 self.CollectionStoragePath = /storage/TiblesOneShotsCollection
728 self.PublicCollectionPath = /public/TiblesOneShotsCollection
729
730 let producer <- create Producer()
731 self.account.storage.save(<-producer, to: self.ProducerStoragePath)
732
733 let cap = self.account.capabilities.storage.issue<&Producer>(self.ProducerStoragePath)
734 self.account.capabilities.publish(cap, at: self.ContentPath)
735 self.contentCapability = self.account.capabilities.get<&Producer>(self.ContentPath)
736
737 emit ContractInitialized()
738 }
739}
740