Smart Contract

Bl0x

A.7620acf6d7f2468a.Bl0x

Deployed

1w ago
Feb 15, 2026, 01:34:02 PM UTC

Dependents

74 imports
1import NonFungibleToken from 0x1d7e57aa55817448
2import MetadataViews from 0x1d7e57aa55817448
3import ViewResolver from 0x1d7e57aa55817448
4
5access(all)
6contract Bl0x: NonFungibleToken{ 
7    access(all)
8    var totalSupply: UInt64
9
10    access(all)
11    event ContractInitialized()
12
13    access(all)
14    event Withdraw(id: UInt64, from: Address?)
15
16    access(all)
17    event Deposit(id: UInt64, to: Address?)
18
19    access(all)
20    event Minted(id: UInt64, address: Address)
21
22    access(all)
23    let CollectionStoragePath: StoragePath
24
25    access(all)
26    let CollectionPublicPath: PublicPath
27
28    access(self)
29    var rarities: [String]
30
31    access(account)
32    let royalties: [MetadataViews.Royalty]
33
34    /*
35    Mythical
36    Legendary
37    Epic
38    Rare
39    Uncommon
40    Common
41    */
42
43    access(all)
44    struct Metadata{ 
45        access(all)
46        let nftId: UInt64
47
48        access(all)
49        let name: String
50
51        access(all)
52        let serial: UInt64
53
54        access(all)
55        let rarity: String
56
57        access(all)
58        let thumbnail: String
59
60        access(all)
61        let image: String
62
63        access(all)
64        let traits: [{String: String}]
65
66        init(nftId: UInt64, name: String, rarity: String, thumbnail: String, image: String, serial: UInt64, traits: [{String: String}]){ 
67            self.nftId = nftId
68            self.name = name
69            self.rarity = rarity
70            self.thumbnail = thumbnail
71            self.image = image
72            self.serial = serial
73            self.traits = traits
74        }
75    }
76
77    //TODO: This can be removed before mainnet
78    access(all)
79    struct Data{ 
80        access(all)
81        let nftId: UInt64
82
83        access(all)
84        let name: String
85
86        access(all)
87        let serial: UInt64
88
89        access(all)
90        let rarity: String
91
92        access(all)
93        let thumbnail: String
94
95        access(all)
96        let image: String
97
98        access(all)
99        let traits:{ String: Trait}
100
101        init(nftId: UInt64, name: String, rarity: String, thumbnail: String, image: String, serial: UInt64, traits:{ String: Trait}){ 
102            self.nftId = nftId
103            self.name = name
104            self.rarity = rarity
105            self.thumbnail = thumbnail
106            self.image = image
107            self.serial = serial
108            self.traits = traits
109        }
110    }
111
112    access(all)
113    resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver{ 
114        access(all)
115        let id: UInt64
116
117        access(all)
118        let serial: UInt64
119
120        access(all)
121        var nounce: UInt64
122
123        access(all)
124        let rootHash: String
125
126        access(all)
127        let season: UInt64
128
129        access(all)
130        let traits:{ String: UInt64}
131
132        access(all)
133        let royalties: MetadataViews.Royalties
134
135        init(serial: UInt64, rootHash: String, season: UInt64, traits:{ String: UInt64}){ 
136            self.nounce = 0
137            self.serial = serial
138            self.id = self.uuid
139            self.rootHash = rootHash
140            self.season = season
141            self.traits = traits
142            self.royalties = MetadataViews.Royalties(Bl0x.royalties)
143        }
144
145        access(all)
146        view fun getViews(): [Type]{ 
147            return [Type<MetadataViews.Display>(), Type<MetadataViews.Medias>(), Type<MetadataViews.Royalties>(), Type<MetadataViews.ExternalURL>(), Type<Data>(), Type<Metadata>(), Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.Rarity>(), Type<MetadataViews.Traits>()]
148        }
149
150        access(all)
151        fun resolveView(_ view: Type): AnyStruct?{ 
152            let imageFile = MetadataViews.IPFSFile(cid: self.rootHash, path: "thumbnail/".concat(self.serial.toString()).concat(".webp"))
153            var fullExtension = ".png"
154            var fullMediaType = "image/png"
155            let traits = self.traits
156            if self.serial == 885{ 
157                traits.remove(key: "Module")
158            }
159            if self.serial == 855{ 
160                traits["Module"] = 244
161            }
162            if traits.containsKey("Module"){ 
163                fullExtension = ".gif"
164                fullMediaType = "image/gif"
165            }
166            let fullFile = MetadataViews.IPFSFile(cid: self.rootHash, path: "fullsize/".concat(self.serial.toString()).concat(fullExtension))
167            let fullMedia = MetadataViews.Media(file: fullFile, mediaType: fullMediaType)
168            let season = self.season
169            let name = "Bl0x Season".concat(season.toString()).concat(" #").concat(self.serial.toString())
170            let description = "Bl0x Season".concat(season.toString())
171            switch view{ 
172            case Type<MetadataViews.Display>():
173                return MetadataViews.Display(name: name, description: description, thumbnail: imageFile)
174            case Type<MetadataViews.ExternalURL>():
175                return MetadataViews.ExternalURL("https://find.xyz/".concat((self.owner!).address.toString()).concat("/collection/bl0x/").concat(self.id.toString()))
176            case Type<MetadataViews.Royalties>():
177                return MetadataViews.Royalties(Bl0x.royalties)
178            case Type<MetadataViews.Medias>():
179                return MetadataViews.Medias([fullMedia])
180            case Type<Data>():
181                return Data(nftId: self.id, name: name, rarity: self.getRarity(), thumbnail: imageFile.uri(), image: fullFile.uri(), serial: self.serial, traits: self.getAllTraitsMetadata())
182            case Type<Metadata>():
183                return Metadata(nftId: self.id, name: name, rarity: self.getRarity(), thumbnail: imageFile.uri(), image: fullFile.uri(), serial: self.serial, traits: self.getAllTraitsMetadataAsArray())
184            case Type<MetadataViews.NFTCollectionDisplay>():
185                let externalURL = MetadataViews.ExternalURL("https://find.xyz/mp/bl0x")
186                let squareImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://bl0x.xyz/assets/home/Bl0xlogo.webp"), mediaType: "image")
187                let bannerImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://pbs.twimg.com/profile_banners/1535883931777892352/1661105339/1500x500"), mediaType: "image")
188                return MetadataViews.NFTCollectionDisplay(name: "bl0x", description: "Minting a Bl0x triggers the catalyst moment of a big bang scenario. Generating a treasure that is designed to relate specifically to its holder.", externalURL: externalURL, squareImage: squareImage, bannerImage: bannerImage, socials:{ "discord": MetadataViews.ExternalURL("https://t.co/iY7AhEumR9"), "twitter": MetadataViews.ExternalURL("https://twitter.com/Bl0xNFT")})
189            case Type<MetadataViews.NFTCollectionData>():
190                return MetadataViews.NFTCollectionData(storagePath: Bl0x.CollectionStoragePath, publicPath: Bl0x.CollectionPublicPath, publicCollection: Type<&Collection>(), publicLinkedType: Type<&Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{ 
191                    return <-Bl0x.createEmptyCollection(nftType: Type<@Bl0x.Collection>())
192                })
193            case Type<MetadataViews.Rarity>():
194                return MetadataViews.Rarity(score: nil, max: nil, description: self.getRarity())
195            case Type<MetadataViews.Traits>():
196                return self.getTraitsAsTraits()
197            }
198            return nil
199        }
200
201        access(account)
202        fun increaseNounce(){ 
203            self.nounce = self.nounce + 1
204        }
205
206        access(all)
207        fun getRarity(): String{ 
208            var traitRarity: [String] = []
209            for trait in self.getAllTraitsMetadata().values{ 
210                traitRarity.append(trait.metadata["rarity"]!)
211            }
212            var rarity = ""
213            for rarityLevel in Bl0x.rarities{ 
214                if traitRarity.contains(rarityLevel){ 
215                    rarity = rarityLevel
216                    break
217                }
218            }
219            return rarity
220        }
221
222        access(all)
223        fun getTraitsAsTraits(): MetadataViews.Traits{ 
224            let traits = self.getAllTraitsMetadata()
225            let mvt: [MetadataViews.Trait] = []
226            for trait in traits.keys{ 
227                let traitValue = traits[trait]!
228                mvt.append(MetadataViews.Trait(name: trait, value: traitValue.getName(), displayType: "String", rarity: MetadataViews.Rarity(score: nil, max: nil, description: traitValue.getRarity())))
229            }
230            return MetadataViews.Traits(mvt)
231        }
232
233        access(all)
234        fun getAllTraitsMetadataAsArray(): [{String: String}]{ 
235            let traits = self.traits
236            if self.serial == 885{ 
237                traits.remove(key: "Module")
238            }
239            if self.serial == 855{ 
240                traits["Module"] = 244
241            }
242            var traitMetadata: [{String: String}] = []
243            for trait in traits.keys{ 
244                let traitId = traits[trait]!
245                traitMetadata.append((Bl0x.traits[traitId]!).metadata)
246            }
247            return traitMetadata
248        }
249
250        access(all)
251        fun getAllTraitsMetadata():{ String: Trait}{ 
252            let traits = self.traits
253            if self.serial == 885{ 
254                traits.remove(key: "Module")
255            }
256            if self.serial == 855{ 
257                traits["Module"] = 244
258            }
259            var traitMetadata:{ String: Trait} ={} 
260            for trait in traits.keys{ 
261                let traitId = traits[trait]!
262                traitMetadata[trait] = Bl0x.traits[traitId]!
263            }
264            return traitMetadata
265        }
266
267        access(all)
268        fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
269            return <-create Collection()
270        }
271    }
272
273    access(all)
274    resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection{ 
275        // dictionary of NFT conforming tokens
276        // NFT is a resource type with an `UInt64` ID field
277        access(all)
278        var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
279
280        init(){ 
281            self.ownedNFTs <-{} 
282        }
283
284        // withdraw removes an NFT from the collection and moves it to the caller
285        access(NonFungibleToken.Withdraw)
286        fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{ 
287            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
288            emit Withdraw(id: token.id, from: self.owner?.address)
289            return <-token
290        }
291
292        // deposit takes a NFT and adds it to the collections dictionary
293        // and adds the ID to the id array
294        access(all)
295        fun deposit(token: @{NonFungibleToken.NFT}): Void{ 
296            let token <- token as! @NFT
297            let id: UInt64 = token.id
298            //TODO: add nounce and emit better event the first time it is moved.
299            token.increaseNounce()
300            // add the new token to the dictionary which removes the old one
301            let oldToken <- self.ownedNFTs[id] <- token
302            emit Deposit(id: id, to: self.owner?.address)
303            destroy oldToken
304        }
305
306        // getIDs returns an array of the IDs that are in the collection
307        access(all)
308        view fun getIDs(): [UInt64]{ 
309            return self.ownedNFTs.keys
310        }
311
312        // borrowNFT gets a reference to an NFT in the collection
313        // so that the caller can read its metadata and call its methods
314        access(all)
315        view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{ 
316            return &self.ownedNFTs[id]
317        }
318
319        access(all)
320        view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?{ 
321            return &self.ownedNFTs[id] 
322        }
323
324        access(all)
325        view fun getSupportedNFTTypes():{ Type: Bool}{ 
326            panic("implement me")
327        }
328
329        access(all)
330        view fun isSupportedNFTType(type: Type): Bool{ 
331            panic("implement me")
332        }
333
334        access(all)
335        fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
336            return <-create Collection()
337        }
338    }
339
340    // public function that anyone can call to create a new empty collection
341    access(all)
342    fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{ 
343        return <-create Collection()
344    }
345
346    // mintNFT mints a new NFT with a new ID
347    // and deposit it in the recipients collection using their collection reference
348    //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
349    //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
350    access(account)
351    fun mintNFT(recipient: &{NonFungibleToken.Receiver}, serial: UInt64, rootHash: String, season: UInt64, traits:{ String: UInt64}){ 
352        pre{ 
353            recipient.owner != nil:
354            "Recipients NFT collection is not owned"
355        }
356        Bl0x.totalSupply = Bl0x.totalSupply + 1
357        // create a new NFT
358        var newNFT <- create NFT(serial: serial, rootHash: rootHash, season: season, traits: traits)
359
360        //Always emit events on state changes! always contain human readable and machine readable information
361        //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
362        //emit Minted(id:newNFT.id, address:recipient.owner!.address)
363        // deposit it in the recipient's account using their reference
364        recipient.deposit(token: <-newNFT)
365    }
366
367    /// A trait contains information about a trait
368    access(all)
369    struct Trait{ 
370        access(all)
371        let id: UInt64
372
373        access(all)
374        let metadata:{ String: String}
375
376        init(id: UInt64, metadata:{ String: String}){ 
377            pre{ 
378                metadata.containsKey("rarity"):
379                "metadata must contain rarity"
380                metadata.containsKey("name"):
381                "metadata must contain name"
382            }
383            self.id = id
384            self.metadata = metadata
385        }
386
387        access(all)
388        fun getName(): String{ 
389            return self.metadata["name"]!
390        }
391
392        access(all)
393        fun getRarity(): String{ 
394            return self.metadata["rarity"]!
395        }
396    }
397
398    access(self)
399    let traits:{ UInt64: Trait}
400
401    access(account)
402    fun addTrait(_ trait: Trait){ 
403        self.traits[trait.id] = trait
404    }
405
406    access(all)
407    fun getTraits():{ UInt64: Trait}{ 
408        return self.traits
409    }
410
411    access(all)
412    fun getTrait(_ id: UInt64): Trait?{ 
413        return self.traits[id]
414    }
415
416    access(account)
417    fun addRoyaltycut(_ cutInfo: MetadataViews.Royalty){ 
418        var cutInfos = self.royalties
419        cutInfos.append(cutInfo)
420        // for validation only
421        let royalties = MetadataViews.Royalties(cutInfos)
422        self.royalties.append(cutInfo)
423    }
424
425    access(all) view fun getContractViews(resourceType: Type?): [Type] {
426        return [
427        Type<MetadataViews.NFTCollectionData>(),
428        Type<MetadataViews.NFTCollectionDisplay>()
429        ]
430    }
431
432    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
433        switch viewType {
434
435        case Type<MetadataViews.NFTCollectionDisplay>():
436            let externalURL = MetadataViews.ExternalURL("https://find.xyz/mp/bl0x")
437            let squareImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://bl0x.xyz/assets/home/Bl0xlogo.webp"), mediaType: "image")
438            let bannerImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://pbs.twimg.com/profile_banners/1535883931777892352/1661105339/1500x500"), mediaType: "image")
439            return MetadataViews.NFTCollectionDisplay(name: "bl0x", description: "Minting a Bl0x triggers the catalyst moment of a big bang scenario. Generating a treasure that is designed to relate specifically to its holder.", externalURL: externalURL, squareImage: squareImage, bannerImage: bannerImage, socials:{ "discord": MetadataViews.ExternalURL("https://t.co/iY7AhEumR9"), "twitter": MetadataViews.ExternalURL("https://twitter.com/Bl0xNFT")})
440        case Type<MetadataViews.NFTCollectionData>():
441            return MetadataViews.NFTCollectionData(storagePath: Bl0x.CollectionStoragePath, publicPath: Bl0x.CollectionPublicPath, publicCollection: Type<&Collection>(), publicLinkedType: Type<&Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{ 
442                return <-Bl0x.createEmptyCollection(nftType: Type<@Bl0x.Collection>())
443            })
444        }
445        return nil
446    }
447
448
449    init(){ 
450        //Rarity (Is there a need to update this?)
451        self.rarities = ["Mythic", "Legendary", "Epic", "Rare", "Uncommon", "Common"]
452        self.traits ={} 
453        // Initialize the total supply
454        self.totalSupply = 0
455
456        // Set Royalty cuts in a transaction
457        self.royalties = []
458
459        // Set the named paths
460        self.CollectionStoragePath = /storage/bl0xNFTs
461        self.CollectionPublicPath = /public/bl0xNFTs
462        self.account.storage.save<@{NonFungibleToken.Collection}>(<-Bl0x.createEmptyCollection(nftType: Type<@Bl0x.Collection>()), to: Bl0x.CollectionStoragePath)
463        var capability_1 = self.account.capabilities.storage.issue<&Bl0x.Collection>(Bl0x.CollectionStoragePath)
464        self.account.capabilities.publish(capability_1, at: Bl0x.CollectionPublicPath)
465        emit ContractInitialized()
466    }
467}
468
469