Smart Contract

PartyFavorz

A.123cb666996b8432.PartyFavorz

Deployed

6d ago
Feb 21, 2026, 06:58:33 PM UTC

Dependents

17 imports
1import NonFungibleToken from 0x1d7e57aa55817448
2import FungibleToken from 0xf233dcee88fe0abe
3import MetadataViews from 0x1d7e57aa55817448
4import FindForge from 0x097bafa4e0b48eef
5import FindPack from 0x097bafa4e0b48eef
6import PartyFavorzExtraData from 0x123cb666996b8432
7import ViewResolver from 0x1d7e57aa55817448
8
9access(all) contract PartyFavorz: NonFungibleToken {
10
11    access(all) var totalSupply: UInt64
12
13    access(all) event ContractInitialized()
14    access(all) event Withdraw(id: UInt64, from: Address?)
15    access(all) event Deposit(id: UInt64, to: Address?)
16    access(all) event Minted(id:UInt64, serial: UInt64, season: UInt64, name: String )
17
18    access(all) let CollectionStoragePath: StoragePath
19    access(all) let CollectionPublicPath: PublicPath
20    access(all) let MinterStoragePath: StoragePath
21
22    access(all) let royalties: [MetadataViews.Royalty]
23
24    access(all) struct Info {
25        access(all) let name: String
26        access(all) let description: String
27        access(all) let thumbnailHash: String
28        access(all) let edition: UInt64
29        access(all) let maxEdition: UInt64
30        access(all) let fullsizeHash: String 
31        access(all) let artist: String
32
33        init(name: String, description: String, thumbnailHash: String, edition:UInt64, maxEdition:UInt64, fullsizeHash: String, artist: String) {
34            self.name=name 
35            self.description=description 
36            self.thumbnailHash=thumbnailHash
37            self.edition=edition
38            self.maxEdition=maxEdition
39            self.fullsizeHash=fullsizeHash
40            self.artist=artist
41        }
42    }
43
44    access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
45        access(all) let id: UInt64
46        access(all) let info: Info
47
48        init(
49            info: Info,
50            season: UInt64,
51            royalties: [MetadataViews.Royalty], 
52            squareImage: String, 
53            bannerImage: String
54        ) {
55            self.id = self.uuid
56            self.info=info
57
58            PartyFavorzExtraData.setData(id: self.id, field: "season", value: season)
59            PartyFavorzExtraData.setData(id: self.id, field: "royalties", value: royalties)
60            PartyFavorzExtraData.setData(id: self.id, field: "nftCollectionDisplay", value: {"squareImage" : squareImage, "bannerImage" : bannerImage})
61        }
62
63        access(all) view fun getID(): UInt64 {
64            return self.id
65        }
66
67        access(all) view fun getViews(): [Type] {
68            return [
69            Type<MetadataViews.Display>(),
70            Type<MetadataViews.Royalties>(),
71            Type<MetadataViews.Editions>(),
72            Type<MetadataViews.Traits>(),
73            Type<MetadataViews.ExternalURL>(),
74            Type<MetadataViews.NFTCollectionData>(),
75            Type<MetadataViews.NFTCollectionDisplay>(), 
76            Type<MetadataViews.Medias>(),
77            Type<FindPack.PackRevealData>()
78            ]
79        }
80
81        access(all) fun resolveView(_ view: Type): AnyStruct? {
82            let imageFile = MetadataViews.IPFSFile( cid: self.info.thumbnailHash, path: nil)
83
84            switch view {
85
86            case Type<FindPack.PackRevealData>():
87                let data : {String : String} = {
88                    "nftImage" : imageFile.uri() ,
89                    "nftName" : self.info.name,
90                    "packType" : "PartyFavorz"
91                }
92                return FindPack.PackRevealData(data)
93
94            case Type<MetadataViews.Display>():
95                return MetadataViews.Display(
96                    name: self.info.name,
97                    description: self.info.description,
98                    thumbnail: MetadataViews.IPFSFile(
99                        cid: self.info.thumbnailHash, path: nil 
100                    )
101                )
102            case Type<MetadataViews.Editions>():
103                let seasonData = PartyFavorzExtraData.getData(id: self.id, field: "season")
104                var season = 1 as UInt64
105                if seasonData != nil {
106                    season = seasonData! as! UInt64
107                }
108                let editionInfo = MetadataViews.Edition(name: "season ".concat(season.toString()), number: self.info.edition, max: self.info.maxEdition)
109                let editionList: [MetadataViews.Edition] = [editionInfo]
110                return MetadataViews.Editions(
111                    editionList
112                )
113            case Type<MetadataViews.Royalties>():
114                let royaltiesData = PartyFavorzExtraData.getData(id: self.id, field: "royalties")
115                if royaltiesData != nil {
116                    let r = royaltiesData! as! [MetadataViews.Royalty]
117                    return MetadataViews.Royalties(r)
118                }
119                return MetadataViews.Royalties(PartyFavorz.royalties)
120
121            case Type<MetadataViews.ExternalURL>():
122                if self.owner != nil {
123                    return MetadataViews.ExternalURL("https://find.xyz/".concat(self.owner!.address.toString()).concat("/collection/partyfavorz/").concat(self.id.toString()))
124                }
125                return MetadataViews.ExternalURL("https://find.xyz/")
126
127            case Type<MetadataViews.NFTCollectionData>():
128                return PartyFavorz.resolveContractView(resourceType: Type<@PartyFavorz.NFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
129            case Type<MetadataViews.NFTCollectionDisplay>():
130                var square = MetadataViews.Media(
131                    file: MetadataViews.IPFSFile(
132                        cid: "QmNkJGEzNYzXsKFqCMweFZBZ9cMQsfMUzV2ZDh2Nn8a1Xc",
133                        path: nil
134                    ),
135                    mediaType: "image/png"
136                )
137
138                var banner = MetadataViews.Media(
139                    file: MetadataViews.IPFSFile(
140                        cid: "QmVuMpDyJXHMCK9LnFboemWfPYabcwPNEmXgQMWbtxtGWD",
141                        path: nil
142                    ),
143                    mediaType: "image/png"
144                )
145
146                let nftCollectionDisplayData = PartyFavorzExtraData.getData(id: self.id, field: "nftCollectionDisplay")
147                if nftCollectionDisplayData != nil {
148                    let nftCollectionDisplay = nftCollectionDisplayData! as! {String : String}
149
150                    square = MetadataViews.Media(
151                        file: MetadataViews.IPFSFile(
152                            cid: nftCollectionDisplay["squareImage"]!,
153                            path: nil
154                        ),
155                        mediaType: "image/png"
156                    )
157
158                    banner = MetadataViews.Media(
159                        file: MetadataViews.IPFSFile(
160                            cid: nftCollectionDisplay["bannerImage"]!,
161                            path: nil
162                        ),
163                        mediaType: "image/png"
164                    )
165                }
166
167                return MetadataViews.NFTCollectionDisplay(
168                    name: "PartyFavorz",
169                    description: "By owning a Party Favorz NFT, you are granted access to the VIP sections of our virtual parties which include, but are not limited to major giveaways, 1 on 1s with artists/project leaders, and some IRL utility that involves partying, down the line. By owning Party Favorz, you are supporting the idea of community coming together for a few goals that include having fun, being positive, learning, and most importantly SUPPORTING ARTISTS.",
170                    externalURL: MetadataViews.ExternalURL("https://find.xyz/partyfavorz"),
171                    squareImage: square,
172                    bannerImage: banner,
173                    socials: {
174                        "twitter": MetadataViews.ExternalURL("https://twitter.com/FlowPartyFavorz"), 
175                        "discord" : MetadataViews.ExternalURL("https://discord.gg/bM76F34EnN")
176                    }
177                )
178                case Type<MetadataViews.Traits>() : 
179                let seasonData = PartyFavorzExtraData.getData(id: self.id, field: "season")
180                var season = 1 as UInt64
181                if seasonData != nil {
182                    season = seasonData! as! UInt64
183                }
184                return MetadataViews.Traits([
185                MetadataViews.Trait(name: "Artist", value: self.info.artist, displayType: "String", rarity: nil) ,
186                MetadataViews.Trait(name: "Season", value: season, displayType: "Numeric", rarity: nil) 
187                ])
188
189                case Type<MetadataViews.Medias>() : 
190                let seasonData = PartyFavorzExtraData.getData(id: self.id, field: "season")
191                var season = 1 as UInt64
192                if seasonData != nil {
193                    season = seasonData! as! UInt64
194                }
195
196                var thumbnailMediaType = "image/png"
197                var fullImageMediaType = "image/png"
198
199                switch season {
200                    case 2: 
201                    fullImageMediaType = "image/gif"
202
203                }
204
205                return MetadataViews.Medias([
206                MetadataViews.Media(file: MetadataViews.IPFSFile(cid: self.info.thumbnailHash, path: nil), mediaType: thumbnailMediaType),
207                MetadataViews.Media(file: MetadataViews.IPFSFile(cid: self.info.fullsizeHash, path: nil), mediaType: fullImageMediaType)
208                ])
209            }
210            return nil
211        }
212
213        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
214            return <-PartyFavorz.createEmptyCollection(nftType:Type<@PartyFavorz.NFT>())
215        }
216    }
217
218    access(all) view fun getContractViews(resourceType: Type?): [Type] {
219        return [
220        Type<MetadataViews.NFTCollectionData>(),
221        Type<MetadataViews.NFTCollectionDisplay>()
222        ]
223    }
224
225    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
226        switch viewType {
227        case Type<MetadataViews.NFTCollectionData>():
228            let collectionRef = self.account.storage.borrow<&PartyFavorz.Collection>(
229                from: PartyFavorz.CollectionStoragePath
230            ) ?? panic("Could not borrow a reference to the stored collection")
231            let collectionData = MetadataViews.NFTCollectionData(
232                storagePath: PartyFavorz.CollectionStoragePath,
233                publicPath: PartyFavorz.CollectionPublicPath,
234                publicCollection: Type<&PartyFavorz.Collection>(),
235                publicLinkedType: Type<&PartyFavorz.Collection>(),
236                createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
237                    return <-PartyFavorz.createEmptyCollection(nftType:Type<@PartyFavorz.NFT>())
238                })
239            )
240            return collectionData
241        }
242        return nil
243    }
244
245    access(all) resource Collection: NonFungibleToken.Collection, ViewResolver.ResolverCollection {
246        // dictionary of NFT conforming tokens
247        // NFT is a resource type with an `UInt64` ID field
248        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
249
250        init () {
251            self.ownedNFTs <- {}
252        }
253
254        // withdraw removes an NFT from the collection and moves it to the caller
255        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
256            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
257
258            emit Withdraw(id: token.id, from: self.owner?.address)
259
260            return <-token
261        }
262
263        // deposit takes a NFT and adds it to the collections dictionary
264        // and adds the ID to the id array
265        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
266            let token <- token as! @PartyFavorz.NFT
267
268            let id: UInt64 = token.id
269
270            // add the new token to the dictionary which removes the old one
271            let oldToken <- self.ownedNFTs[id] <- token
272
273            emit Deposit(id: id, to: self.owner?.address)
274
275            destroy oldToken
276        }
277
278        // getIDs returns an array of the IDs that are in the collection
279        access(all) view fun getIDs(): [UInt64] {
280            return self.ownedNFTs.keys
281        }
282
283        // borrowNFT gets a reference to an NFT in the collection
284        // so that the caller can read its metadata and call its methods
285        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
286            return (&self.ownedNFTs[id])
287        }
288
289        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver} {
290            let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
291            let PartyFavorz = nft as! &PartyFavorz.NFT
292            return PartyFavorz
293        }
294
295        access(all) view fun getLength(): Int {
296            return self.ownedNFTs.keys.length
297        }
298
299        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
300            return <- create PartyFavorz.Collection()
301        }
302
303        /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
304        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
305            let supportedTypes: {Type: Bool} = {}
306            supportedTypes[Type<@PartyFavorz.NFT>()] = true
307            return supportedTypes
308        }
309
310        /// Return the default storage path for the collection
311        access(all) view fun getDefaultStoragePath(): StoragePath? {
312            return PartyFavorz.CollectionStoragePath
313
314        }
315
316        /// Return the default public path for the collection
317        access(all) view fun getDefaultPublicPath(): PublicPath? {
318            return PartyFavorz.CollectionPublicPath
319        }
320
321        /// Returns whether or not the given type is accepted by the collection
322        /// A collection that can accept any type should just return true by default
323        access(all) view fun isSupportedNFTType(type: Type): Bool {
324            if type == Type<@PartyFavorz.NFT>() {
325                return true
326            } else {
327                return false
328            }
329        }
330    }
331
332    // public function that anyone can call to create a new empty collection
333    access(all) fun createEmptyCollection(nftType:Type): @{NonFungibleToken.Collection} {
334        return <- create Collection()
335    }
336
337    access(all) resource Forge: FindForge.Forge {
338        access(FindForge.ForgeOwner) fun mint(platform: FindForge.MinterPlatform, data: AnyStruct, verifier: &FindForge.Verifier) : @{NonFungibleToken.NFT} {
339            let info = data as? {String : AnyStruct} ?? panic("The data passed in is not in form as needed.")
340
341            assert(info.length == 5, message: "Please make sure to pass in `Info, season, royalties, squareImage, bannerImage`")
342
343            // create a new NFT
344            var newNFT <- create NFT(
345                info: info["info"]! as! Info, 
346                season: info["season"]! as! UInt64,
347                royalties: info["royalties"]! as! [MetadataViews.Royalty], 
348                squareImage: info["squareImage"]! as! String, 
349                bannerImage: info["bannerImage"]! as! String
350            )
351
352            PartyFavorz.totalSupply = PartyFavorz.totalSupply + 1
353            emit Minted(id:newNFT.id, serial: PartyFavorz.totalSupply, season: info["season"]! as! UInt64 , name: newNFT.info.name )
354            return <- newNFT
355        }
356
357
358        access(FindForge.ForgeOwner) fun addContractData(platform: FindForge.MinterPlatform, data: AnyStruct, verifier: &FindForge.Verifier) {
359            // not used here 
360            panic("Not supported for PartyFavorz Contract") 
361        }
362    }
363
364    access(all) fun getForgeType() : Type {
365        return Type<@Forge>()
366    }
367
368    init() {
369        // Initialize the total supply
370        self.totalSupply = 0
371
372        // Set the named paths
373        self.CollectionStoragePath = /storage/PartyFavorzCollection
374        self.CollectionPublicPath = /public/PartyFavorzCollection
375        self.MinterStoragePath = /storage/PartyFavorzMinter
376
377        // TODO: Fix this to run on tests
378        let partyFavorz = self.account.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
379        let artist = self.account.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
380
381        self.royalties = [
382        MetadataViews.Royalty(receiver: partyFavorz, cut: 0.03, description: "Party Favorz"), 
383        MetadataViews.Royalty(receiver: artist, cut: 0.02, description: "Artist") 
384        ]
385
386        // Create a Collection resource and save it to storage
387        let collection <- create Collection()
388        self.account.storage.save(<-collection, to: self.CollectionStoragePath)
389        let collectionCap = self.account.capabilities.storage.issue<&{NonFungibleToken.Collection}>(self.CollectionStoragePath)
390        self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
391
392        FindForge.addForgeType(<- create Forge())
393        emit ContractInitialized()
394    }
395}
396
397
398