Smart Contract
GeneratedExperiences
A.123cb666996b8432.GeneratedExperiences
1import NonFungibleToken from 0x1d7e57aa55817448
2import FungibleToken from 0xf233dcee88fe0abe
3import MetadataViews from 0x1d7e57aa55817448
4import ViewResolver from 0x1d7e57aa55817448
5import FindForge from 0x097bafa4e0b48eef
6import FindPack from 0x097bafa4e0b48eef
7
8access(all) contract GeneratedExperiences: NonFungibleToken {
9
10 access(all) var totalSupply: UInt64
11
12 access(all) event ContractInitialized()
13 access(all) event Withdraw(id: UInt64, from: Address?)
14 access(all) event Deposit(id: UInt64, to: Address?)
15 access(all) event Minted(id:UInt64, season: UInt64, name: String, thumbnail: String, fullsize: String, artist: String, rarity: String, edition: UInt64, maxEdition: UInt64)
16 access(all) event SeasonAdded(season:UInt64, squareImage: String, bannerImage: String)
17
18 access(all) let CollectionStoragePath: StoragePath
19 access(all) let CollectionPrivatePath: PrivatePath
20 access(all) let CollectionPublicPath: PublicPath
21 access(all) let MinterStoragePath: StoragePath
22
23 access(all) let CollectionName : String
24
25 access(all) let collectionInfo: {UInt64 : CollectionInfo}
26
27 access(all) struct CollectionInfo {
28 access(all) let season: UInt64
29 access(all) var royalties: [MetadataViews.Royalty]
30 // This is only used internally for fetching royalties in
31 access(all) let royaltiesInput: [FindPack.Royalty]
32 access(all) let squareImage: MetadataViews.Media
33 access(all) let bannerImage: MetadataViews.Media
34 access(all) let description: String
35 access(all) let socials: {String : String}
36 access(all) let extra: {String: AnyStruct}
37
38 init(
39 season: UInt64,
40 royalties: [MetadataViews.Royalty],
41 royaltiesInput: [FindPack.Royalty],
42 squareImage: MetadataViews.Media,
43 bannerImage: MetadataViews.Media,
44 socials: {String : String},
45 description: String
46 ) {
47 self.season = season
48 self.royalties = royalties
49 self.royaltiesInput = royaltiesInput
50 self.squareImage = squareImage
51 self.bannerImage = bannerImage
52 self.description = description
53 self.socials = socials
54 self.extra={}
55 }
56
57 // This is only used internally for fetching royalties in
58 access(contract) fun setRoyalty(r: [MetadataViews.Royalty]) {
59 self.royalties = r
60 }
61 }
62
63 access(all) struct Info {
64 access(all) let season: UInt64
65 access(all) let name: String
66 access(all) let description: String
67 access(all) let thumbnail: {MetadataViews.File}
68 access(all) let fullsize: {MetadataViews.File}
69 access(all) let edition: UInt64
70 access(all) let maxEdition: UInt64
71 access(all) let artist: String
72 access(all) let rarity: String
73 access(self) let extra: {String: AnyStruct}
74
75 init(season: UInt64, name: String, description: String, thumbnail: {MetadataViews.File}, edition:UInt64, maxEdition:UInt64, fullsize: {MetadataViews.File}, artist: String, rarity: String) {
76 self.season=season
77 self.name=name
78 self.description=description
79 self.thumbnail=thumbnail
80 self.fullsize=fullsize
81 self.edition=edition
82 self.maxEdition=maxEdition
83 self.artist=artist
84 self.rarity=rarity
85 self.extra={}
86 }
87 }
88
89
90 access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
91 access(all) let id: UInt64
92 access(all) let info: Info
93
94 init(
95 info: Info
96 ) {
97 self.id = self.uuid
98 self.info=info
99 }
100
101
102
103 access(all) view fun getID(): UInt64 {
104 return self.id
105 }
106
107 access(all) view fun getViews(): [Type] {
108 return [
109 Type<MetadataViews.Display>(),
110 Type<MetadataViews.Royalties>(),
111 Type<MetadataViews.Editions>(),
112 Type<MetadataViews.Traits>(),
113 Type<MetadataViews.ExternalURL>(),
114 Type<MetadataViews.NFTCollectionData>(),
115 Type<MetadataViews.NFTCollectionDisplay>(),
116 Type<MetadataViews.Medias>(),
117 Type<MetadataViews.Rarity>(),
118 Type<FindPack.PackRevealData>()
119 ]
120 }
121
122 access(all) fun resolveView(_ view: Type): AnyStruct? {
123
124 let collection = GeneratedExperiences.collectionInfo[self.info.season]!
125
126 switch view {
127
128 case Type<FindPack.PackRevealData>():
129 let data : {String : String} = {
130 "nftImage" : self.info.thumbnail.uri() ,
131 "nftName" : self.info.name,
132 "packType" : GeneratedExperiences.CollectionName
133 }
134 return FindPack.PackRevealData(data)
135
136 case Type<MetadataViews.Display>():
137 return MetadataViews.Display(
138 name: self.info.name,
139 description: self.info.description,
140 thumbnail: self.info.thumbnail
141 )
142 case Type<MetadataViews.Editions>():
143 // We do not show season here unless there are more than 1 collectionInfo (that is indexed by season)
144 let editionName = GeneratedExperiences.CollectionName.toLower()
145 let editionInfo = MetadataViews.Edition(name: editionName, number: self.info.edition, max: self.info.maxEdition)
146 let editionList: [MetadataViews.Edition] = [editionInfo]
147 return MetadataViews.Editions(
148 editionList
149 )
150 case Type<MetadataViews.Royalties>():
151 return MetadataViews.Royalties(collection.royalties)
152
153 case Type<MetadataViews.ExternalURL>():
154 if self.owner != nil {
155 return MetadataViews.ExternalURL("https://find.xyz/".concat(self.owner!.address.toString()).concat("/collection/main/").concat(GeneratedExperiences.CollectionName).concat("/").concat(self.id.toString()))
156 }
157 return MetadataViews.ExternalURL("https://find.xyz/")
158
159 case Type<MetadataViews.NFTCollectionData>():
160 return GeneratedExperiences.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>())
161
162 case Type<MetadataViews.NFTCollectionDisplay>():
163 return GeneratedExperiences.getCollectionDisplay(self.info.season)
164
165 case Type<MetadataViews.Traits>() :
166
167 let traits = [
168 MetadataViews.Trait(name: "Artist", value: self.info.artist, displayType: "String", rarity: nil)
169 ]
170
171 if GeneratedExperiences.collectionInfo.length > 1 {
172 traits.append(MetadataViews.Trait(name: "Season", value: self.info.season, displayType: "Numeric", rarity: nil))
173 }
174
175 return MetadataViews.Traits(traits)
176
177 case Type<MetadataViews.Medias>() :
178 return MetadataViews.Medias([
179 MetadataViews.Media(file: self.info.thumbnail, mediaType: "image"),
180 MetadataViews.Media(file: self.info.fullsize, mediaType: "image")
181 ])
182
183 case Type<MetadataViews.Rarity>() :
184 return MetadataViews.Rarity(score: nil, max: nil, description: self.info.rarity)
185 }
186 return nil
187 }
188
189 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
190 return <-GeneratedExperiences.createEmptyCollection(nftType:Type<@GeneratedExperiences.NFT>())
191 }
192 }
193
194 access(all) view fun getContractViews(resourceType: Type?): [Type] {
195 return [
196 Type<MetadataViews.NFTCollectionData>(),
197 Type<MetadataViews.NFTCollectionDisplay>()
198 ]
199 }
200
201 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
202 switch viewType {
203 case Type<MetadataViews.NFTCollectionData>():
204 let collectionRef = self.account.storage.borrow<&GeneratedExperiences.Collection>(
205 from: GeneratedExperiences.CollectionStoragePath
206 ) ?? panic("Could not borrow a reference to the stored collection")
207 let collectionData = MetadataViews.NFTCollectionData(
208 storagePath: GeneratedExperiences.CollectionStoragePath,
209 publicPath: GeneratedExperiences.CollectionPublicPath,
210 publicCollection: Type<&GeneratedExperiences.Collection>(),
211 publicLinkedType: Type<&GeneratedExperiences.Collection>(),
212 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
213 return <-GeneratedExperiences.createEmptyCollection(nftType:Type<@GeneratedExperiences.NFT>())
214 })
215 )
216 return collectionData
217 }
218 return nil
219 }
220
221 access(all) resource Collection: NonFungibleToken.Collection, ViewResolver.ResolverCollection {
222 /// dictionary of NFT conforming tokens
223 /// NFT is a resource type with an `UInt64` ID field
224 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
225
226 /// Return the default storage path for the collection
227 access(all) view fun getDefaultStoragePath(): StoragePath? {
228 return GeneratedExperiences.CollectionStoragePath
229 }
230
231 /// Return the default public path for the collection
232 access(all) view fun getDefaultPublicPath(): PublicPath? {
233 return GeneratedExperiences.CollectionPublicPath
234 }
235
236 init () {
237 self.ownedNFTs <- {}
238 }
239
240 /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
241 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
242 let supportedTypes: {Type: Bool} = {}
243 supportedTypes[Type<@GeneratedExperiences.NFT>()] = true
244 return supportedTypes
245 }
246
247 /// Returns whether or not the given type is accepted by the collection
248 /// A collection that can accept any type should just return true by default
249 access(all) view fun isSupportedNFTType(type: Type): Bool {
250 if type == Type<@GeneratedExperiences.NFT>() {
251 return true
252 } else {
253 return false
254 }
255 }
256
257 /// withdraw removes an NFT from the collection and moves it to the caller
258 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
259 let token <- self.ownedNFTs.remove(key: withdrawID)
260 ?? panic("Could not withdraw an NFT with the provided ID from the collection")
261
262 return <-token
263 }
264
265 /// deposit takes a NFT and adds it to the collections dictionary
266 /// and adds the ID to the id array
267 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
268 let token <- token as! @GeneratedExperiences.NFT
269
270 // add the new token to the dictionary which removes the old one
271 let oldToken <- self.ownedNFTs[token.id] <- token
272
273 destroy oldToken
274 }
275
276 /// getIDs returns an array of the IDs that are in the collection
277 access(all) view fun getIDs(): [UInt64] {
278 return self.ownedNFTs.keys
279 }
280
281 /// Gets the amount of NFTs stored in the collection
282 access(all) view fun getLength(): Int {
283 return self.ownedNFTs.keys.length
284 }
285
286 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
287 return &self.ownedNFTs[id]
288 }
289
290 /// Borrow the view resolver for the specified NFT ID
291 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
292 if let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?) as! &GeneratedExperiences.NFT? {
293 return nft as &{ViewResolver.Resolver}
294 }
295 return nil
296 }
297
298 /// public function that anyone can call to create a new empty collection
299 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
300
301 return <- create GeneratedExperiences.Collection()
302 }
303 }
304
305
306 // public function that anyone can call to create a new empty collection
307 access(all) fun createEmptyCollection(nftType:Type): @GeneratedExperiences.Collection {
308 return <- create Collection()
309 }
310
311
312 access(all) view fun getViews(): [Type] {
313 return [ Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>() ]
314 }
315
316
317 access(all) view fun getCollectionDisplay(_ season: UInt64): MetadataViews.NFTCollectionDisplay? {
318 let collection = GeneratedExperiences.collectionInfo[season]!
319
320 var square = collection.squareImage
321
322 var banner = collection.bannerImage
323
324 let social : {String : MetadataViews.ExternalURL} = {}
325 for s in collection.socials.keys {
326 social[s] = MetadataViews.ExternalURL(collection.socials[s]!)
327 }
328
329 return MetadataViews.NFTCollectionDisplay(
330 name: GeneratedExperiences.CollectionName,
331 description: collection.description,
332 externalURL: MetadataViews.ExternalURL("https://find.xyz/mp/".concat(GeneratedExperiences.CollectionName)),
333 squareImage: square,
334 bannerImage: banner,
335 socials: social
336 )
337
338 }
339
340 access(all) resource Forge: FindForge.Forge {
341 access(FindForge.ForgeOwner) fun mint(platform: FindForge.MinterPlatform, data: AnyStruct, verifier: &FindForge.Verifier) : @{NonFungibleToken.NFT} {
342 let info = data as? Info ?? panic("The data passed in is not in form as needed. Needed: ".concat(Type<Info>().identifier))
343
344 // create a new NFT
345 var newNFT <- create NFT(
346 info: info,
347 )
348
349 GeneratedExperiences.totalSupply = GeneratedExperiences.totalSupply + 1
350 emit Minted(id:newNFT.id, season: info.season, name: info.name, thumbnail: info.thumbnail.uri(), fullsize: info.fullsize.uri(), artist: info.artist, rarity: info.rarity, edition: info.edition, maxEdition: info.maxEdition)
351 return <- newNFT
352 }
353
354 access(FindForge.ForgeOwner) fun addContractData(platform: FindForge.MinterPlatform, data: AnyStruct, verifier: &FindForge.Verifier) {
355 let collectionInfo = data as? CollectionInfo ?? panic("The data passed in is not in form as needed. Needed: ".concat(Type<CollectionInfo>().identifier))
356
357 // We cannot send in royalties directly, therefore we have to send in FindPack Royalties and generate it during minting
358 let arr : [MetadataViews.Royalty] = []
359 for r in collectionInfo.royaltiesInput {
360 // Try to get Token Switchboard
361 var receiverCap = getAccount(r.recipient).capabilities.get<&{FungibleToken.Receiver}>(/public/GenericFTReceiver)
362
363 if receiverCap == nil || !receiverCap!.check() {
364 receiverCap = getAccount(r.recipient).capabilities.get<&{FungibleToken.Receiver}>(/public/findProfileReceiver)
365 }
366
367 arr.append(MetadataViews.Royalty(receiver: receiverCap!, cut: r.cut, description: r.description))
368 }
369 collectionInfo.setRoyalty(r: arr)
370
371 GeneratedExperiences.collectionInfo[collectionInfo.season] = collectionInfo
372 emit SeasonAdded(season:collectionInfo.season, squareImage: collectionInfo.squareImage.file.uri(), bannerImage: collectionInfo.bannerImage.file.uri())
373 }
374 }
375
376 access(all) fun getForgeType() : Type {
377 return Type<@Forge>()
378 }
379
380 init() {
381 self.CollectionName = "GeneratedExperiences"
382 // Initialize the total supply
383 self.totalSupply = 0
384
385 // Set the named paths
386 self.CollectionStoragePath = StoragePath(identifier: self.CollectionName)!
387 self.CollectionPrivatePath = PrivatePath(identifier: self.CollectionName)!
388 self.CollectionPublicPath = PublicPath(identifier: self.CollectionName)!
389 self.MinterStoragePath = StoragePath(identifier: self.CollectionName.concat("Minter"))!
390
391 self.collectionInfo = {}
392
393 // Create a Collection resource and save it to storage
394 let collection <- create Collection()
395 self.account.storage.save(<-collection, to: self.CollectionStoragePath)
396 let cap = self.account.capabilities.storage.issue<&GeneratedExperiences.Collection>(GeneratedExperiences.CollectionStoragePath)
397 self.account.capabilities.publish(cap, at: GeneratedExperiences.CollectionPublicPath)
398
399 FindForge.addForgeType(<- create Forge())
400 emit ContractInitialized()
401 }
402}
403
404
405