Smart Contract

Flomies

A.123cb666996b8432.Flomies

Deployed

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

Dependents

0 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import MetadataViews from 0x1d7e57aa55817448
4import FindForge from 0x097bafa4e0b48eef
5import FindPack from 0x097bafa4e0b48eef
6import ViewResolver from 0x1d7e57aa55817448
7
8
9access(all) contract Flomies: NonFungibleToken{
10
11    access(all) var totalSupply: UInt64
12
13    access(all) event ContractInitialized()
14    access(all) event Minted(id:UInt64, serial: UInt64, traits: [UInt64])
15    access(all) event RegisteredTraits(traitId:UInt64, trait:{String : String})
16
17    access(all) let CollectionStoragePath: StoragePath
18    access(all) let CollectionPublicPath: PublicPath
19
20    access(account) var royalties : [MetadataViews.Royalty]
21    access(self) let traits : {UInt64: MetadataViews.Trait}
22
23    /*
24    Iconic
25    Legendary
26    Rare
27    Common
28    */
29
30    access(all) struct Metadata {
31        access(all) let nftId: UInt64
32        access(all) let name: String
33        access(all) let serial:UInt64
34        access(all) let thumbnail: String
35        access(all) let image: String
36        access(all) let traits: [UInt64]
37
38        init(nftId: UInt64,name:String,thumbnail: String, image:String, serial:UInt64, traits: [UInt64]) {
39            self.nftId=nftId
40            self.name=name
41            self.thumbnail=thumbnail
42            self.image=image
43            self.serial=serial
44            self.traits=traits
45        }
46    }
47
48    access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
49
50        access(all) let id:UInt64
51        access(all) let serial:UInt64
52        access(all) var nounce:UInt64
53        access(all) let rootHash:String
54        access(all) let traits: [UInt64]
55
56        init(
57            serial:UInt64,
58            rootHash:String,
59            traits: [UInt64]
60        ) {
61            self.nounce=0
62            self.serial=serial
63            self.id=self.uuid
64            self.rootHash=rootHash
65            self.traits=traits
66        }
67
68        access(all) view fun getViews(): [Type] {
69            return  [
70            Type<MetadataViews.Display>(),
71            Type<MetadataViews.Medias>(),
72            Type<MetadataViews.Royalties>(),
73            Type<MetadataViews.ExternalURL>(),
74            Type<Metadata>(),
75            Type<MetadataViews.NFTCollectionData>(),
76            Type<MetadataViews.NFTCollectionDisplay>(),
77            Type<MetadataViews.Traits>(), 
78            Type<FindPack.PackRevealData>(), 
79            Type<MetadataViews.Editions>(), 
80            Type<MetadataViews.Serial>()
81            ]
82        }
83
84        access(all) view fun getID(): UInt64 {
85            return self.id
86        }
87
88        access(all) fun resolveView(_ view: Type): AnyStruct? {
89
90            let imageFile=MetadataViews.IPFSFile( cid: self.rootHash, path: self.serial.toString().concat(".png"))
91            var fullMediaType="image/png"
92            let traits = self.traits
93
94            let fullMedia=MetadataViews.Media(file:imageFile, mediaType: fullMediaType)
95
96            let name ="Flomies #".concat(self.serial.toString())
97            let description= "Flomies is a collection of 3333 homies living on the flow blockchain. Flomies are about art, mental health and innovating in this ecosystem. Our adventure is unique, as is our community."
98
99            switch view {
100            case Type<MetadataViews.Display>():
101                return MetadataViews.Display(
102                    name: name,
103                    description: description,
104                    thumbnail: imageFile
105                )
106
107            case Type<MetadataViews.ExternalURL>():
108                if self.owner == nil {
109                    return MetadataViews.ExternalURL("https://find.xyz/")
110                }
111                return MetadataViews.ExternalURL("https://find.xyz/".concat(self.owner!.address.toString()).concat("/collection/flomies/").concat(self.id.toString()))
112
113            case Type<MetadataViews.Royalties>():
114                return MetadataViews.Royalties(Flomies.royalties)
115
116            case Type<MetadataViews.Medias>():
117                return MetadataViews.Medias([fullMedia])
118
119            case Type<Metadata>():
120                return Metadata(
121                    nftId : self.id ,
122                    name : name ,
123                    thumbnail : imageFile.uri(),
124                    image : imageFile.uri(),
125                    serial:self.serial,
126                    traits:self.traits
127                )
128            case Type<MetadataViews.NFTCollectionDisplay>():
129                return Flomies.resolveContractView(resourceType: Type<@Flomies.NFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>()) as! MetadataViews.NFTCollectionDisplay
130
131            case Type<MetadataViews.NFTCollectionData>():
132                return Flomies.resolveContractView(resourceType: Type<@Flomies.NFT>(), viewType: Type<MetadataViews.NFTCollectionData>()) as! MetadataViews.NFTCollectionData
133
134            case Type<MetadataViews.Traits>():
135                return MetadataViews.Traits(self.getAllTraitsMetadataAsArray())
136
137
138            case Type<FindPack.PackRevealData>():
139                let data : {String : String} = {
140                    "nftImage" : imageFile.uri() ,
141                    "nftName" : "Flomies ".concat(self.serial.toString()), 
142                    "packType" : "Flomies"
143                }
144                return FindPack.PackRevealData(data)
145
146                case Type<MetadataViews.Editions>() : 
147                return MetadataViews.Editions([
148                MetadataViews.Edition(name: "set", number: self.serial, max: 3333)
149                ])
150
151                case Type<MetadataViews.Serial>() : 
152                return MetadataViews.Serial(self.serial)
153            }
154
155            return nil
156        }
157
158        access(contract) fun increaseNounce() {
159            self.nounce=self.nounce+1
160        }
161
162        access(all) fun getAllTraitsMetadataAsArray() : [MetadataViews.Trait] {
163            let traits = self.traits
164
165            var traitMetadata : [MetadataViews.Trait] = []
166            for trait in traits {
167                traitMetadata.append(Flomies.traits[trait]!)
168            }
169            return traitMetadata
170        }
171
172        access(all) fun getAllTraitsMetadata() : {String : MetadataViews.Trait} {
173            let traitMetadata : {String : MetadataViews.Trait} = {}
174            for trait in self.getAllTraitsMetadataAsArray() {
175                let traitName = trait.name
176                traitMetadata[traitName] = trait
177            }
178            return traitMetadata
179        }
180
181        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
182            return <-Flomies.createEmptyCollection(nftType:Type<@Flomies.NFT>())
183        }
184    }
185
186    access(all) view fun getContractViews(resourceType: Type?): [Type] {
187        return [
188        Type<MetadataViews.NFTCollectionData>(),
189        Type<MetadataViews.NFTCollectionDisplay>()
190        ]
191    }
192
193    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
194        switch viewType {
195        case Type<MetadataViews.NFTCollectionData>():
196            let collectionRef = self.account.storage.borrow<&Flomies.Collection>(
197                from: Flomies.CollectionStoragePath
198            ) ?? panic("Could not borrow a reference to the stored collection")
199            let collectionData = MetadataViews.NFTCollectionData(
200                storagePath: Flomies.CollectionStoragePath,
201                publicPath: Flomies.CollectionPublicPath,
202                publicCollection: Type<&Flomies.Collection>(),
203                publicLinkedType: Type<&Flomies.Collection>(),
204                createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
205                    return <-Flomies.createEmptyCollection(nftType:Type<@Flomies.NFT>())
206                })
207            )
208            return collectionData
209        case Type<MetadataViews.NFTCollectionDisplay>():
210            let externalURL = MetadataViews.ExternalURL("https://flomiesnft.com")
211            let squareImage = MetadataViews.Media(file: MetadataViews.IPFSFile(cid: "QmYtowktCz6GbP6MqMd6SXqJEYazCpGTcFm4HrWX89nUvo", path: nil), mediaType: "image/png")
212            let bannerImage = MetadataViews.Media(file: MetadataViews.IPFSFile(cid: "QmPeZUjsfrFvkB1bvKBpAsxfoQ6jSoezqwWz9grkmNYdz1", path: nil), mediaType: "image/png")
213            return MetadataViews.NFTCollectionDisplay(name: "flomies", 
214            description: "Flomies is a collection of 3333 homies living on the flow blockchain. Flomies are about art, mental health and innovating in this ecosystem. Our adventure is unique, as is our community.", 
215            externalURL: externalURL, 
216            squareImage: squareImage, 
217            bannerImage: bannerImage, 
218            socials: { 
219                "discord": MetadataViews.ExternalURL("https://discord.gg/tVavHtPD"), 
220                "twitter" : MetadataViews.ExternalURL("https://twitter.com/flomiesnft"),
221                "instagram" : MetadataViews.ExternalURL("https://www.instagram.com/flomies_nft/")
222            })
223        }
224        return nil
225    }
226
227    access(all) resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, ViewResolver.ResolverCollection {
228        // dictionary of NFT conforming tokens
229        // NFT is a resource type with an `UInt64` ID field
230        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
231
232        init () {
233            self.ownedNFTs <- {}
234        }
235
236        // withdraw removes an NFT from the collection and moves it to the caller
237        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
238            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
239
240            return <-token
241        }
242
243        // deposit takes a NFT and adds it to the collections dictionary
244        // and adds the ID to the id array
245        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
246            let token <- token as! @NFT
247
248            let id: UInt64 = token.id
249            //TODO: add nounce and emit better event the first time it is moved.
250
251            token.increaseNounce()
252            // add the new token to the dictionary which removes the old one
253            let oldToken <- self.ownedNFTs[id] <- token
254
255
256            destroy oldToken
257        }
258
259        // getIDs returns an array of the IDs that are in the collection
260        access(all) view fun getIDs(): [UInt64] {
261            return self.ownedNFTs.keys
262        }
263
264        access(all) view fun getLength(): Int {
265            return self.ownedNFTs.keys.length
266        }
267
268        // borrowNFT gets a reference to an NFT in the collection
269        // so that the caller can read its metadata and call its methods
270        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
271            return &self.ownedNFTs[id]
272        }
273
274        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver} {
275            let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
276            let flomies = nft as! &NFT
277            return flomies
278        }
279
280        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
281            return <- create Flomies.Collection()
282        }
283
284        /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
285        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
286            let supportedTypes: {Type: Bool} = {}
287            supportedTypes[Type<@Flomies.NFT>()] = true
288            return supportedTypes
289        }
290
291  
292
293        /// Returns whether or not the given type is accepted by the collection
294        /// A collection that can accept any type should just return true by default
295        access(all) view fun isSupportedNFTType(type: Type): Bool {
296            if type == Type<@Flomies.NFT>() {
297                return true
298            } else {
299                return false
300            }
301        }
302    }
303
304
305    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
306        return <- create Collection()
307    }
308
309    // mintNFT mints a new NFT with a new ID
310    // and deposit it in the recipients collection using their collection reference
311    //The distinction between sending in a reference and sending in a capability is that when you send in a reference it cannot be stored. So it can only be used in this method
312    //while a capability can be stored and used later. So in this case using a reference is the right choice, but it needs to be owned so that you can have a good event
313    access(account) fun mintNFT( 
314        serial:UInt64,
315        rootHash:String,
316        traits: [UInt64]
317    ) : @{NonFungibleToken.NFT} {
318
319        Flomies.totalSupply = Flomies.totalSupply + 1
320        // create a new NFT
321        var newNFT <- create NFT(
322            serial:serial,
323            rootHash:rootHash,
324            traits: traits)
325
326            emit Minted(id: newNFT.uuid, serial: newNFT.serial, traits: traits)
327
328            //Always emit events on state changes! always contain human readable and machine readable information
329            //TODO: discuss that fields we want in this event. Or do we prefer to use the richer deposit event, since this is really done in the backend
330            //emit Minted(id:newNFT.id, address:recipient.owner!.address)
331            // deposit it in the recipient's account using their reference
332            return <-newNFT
333
334        }
335
336        access(account) fun addTrait(_ traits: {UInt64 : MetadataViews.Trait}) {
337            for key in traits.keys {
338                let trait = traits[key]!
339                self.traits[key]=trait
340                let traits : {String : String} = {}
341                traits["name"] = trait.name 
342                traits["value"] = trait.value as! String 
343                traits["rarity_description"] = trait.rarity?.description
344                traits["rarity_score"] = trait.rarity?.score?.toString()
345                traits["rarity_max"] = trait.rarity?.max?.toString()
346
347                emit RegisteredTraits(traitId: key, trait:traits)
348            }
349        }
350
351        access(all) fun getTraits() : {UInt64:MetadataViews.Trait}{
352            return self.traits
353        }
354
355        access(all) fun getTrait(_ id:UInt64) : MetadataViews.Trait? {
356            return self.traits[id]
357        }
358
359        access(account) fun addRoyaltycut(_ cutInfo: [MetadataViews.Royalty]) {
360            var cutInfos = self.royalties 
361            cutInfos.appendAll(cutInfo)
362            // for validation only
363            let royalties = MetadataViews.Royalties(cutInfos)
364            self.royalties.appendAll(cutInfo)
365        }
366
367        access(all) resource Forge: FindForge.Forge {
368            access(FindForge.ForgeOwner) fun mint(platform: FindForge.MinterPlatform, data: AnyStruct, verifier: &FindForge.Verifier) : @{NonFungibleToken.NFT} {
369                let info = data as? {String : AnyStruct} ?? panic("The data passed in is not in form of {String : AnyStruct}")
370
371                let serial = info["serial"]! as? UInt64 ?? panic("Serial is missing")
372                let rootHash = info["rootHash"]! as? String ?? panic("RootHash is missing")
373                let traits = info["traits"]! as? [UInt64] ?? panic("traits are missing")
374
375                return <- Flomies.mintNFT( 
376                    serial:serial,
377                    rootHash:rootHash,
378                    traits:traits
379                )
380            }
381
382            access(FindForge.ForgeOwner) fun addContractData(platform: FindForge.MinterPlatform, data: AnyStruct, verifier: &FindForge.Verifier) {
383                let type = data.getType() 
384
385                switch type {
386                    case Type<{UInt64 : MetadataViews.Trait}>() : 
387                    // for duplicated indexes, the new one will replace the old one 
388                    let typedData = data as! {UInt64 : MetadataViews.Trait}
389                    Flomies.addTrait(typedData)
390                    return
391
392                    case Type<[MetadataViews.Royalty]>() : 
393                    let typedData = data as! [MetadataViews.Royalty]
394                    Flomies.royalties = typedData
395                    return
396
397                }
398            }
399        }
400
401        access(account) fun createForge() : @{FindForge.Forge} {
402            return <- create Forge()
403        }
404
405        init() {
406            self.traits={}
407            // Initialize the total supply
408            self.totalSupply = 0
409
410            self.royalties = []
411
412            // Set the named paths
413            self.CollectionStoragePath = /storage/flomiesNFT
414            self.CollectionPublicPath = /public/flomiesNFT
415
416            let collection <- create Collection()
417            self.account.storage.save(<-collection, to: self.CollectionStoragePath)
418            let collectionCap = self.account.capabilities.storage.issue<&{NonFungibleToken.Collection}>(self.CollectionStoragePath)
419            self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
420
421            FindForge.addForgeType(<- create Forge())
422
423            emit ContractInitialized()
424        }
425    }
426
427