Smart Contract
Flomies
A.123cb666996b8432.Flomies
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