Smart Contract

GeneratedExperiences

A.123cb666996b8432.GeneratedExperiences

Deployed

16h ago
Feb 28, 2026, 02:30:51 AM UTC

Dependents

0 imports
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