Smart Contract

DimensionX

A.e3ad6030cbaff1c2.DimensionX

Deployed

2w ago
Feb 11, 2026, 06:35:21 PM UTC

Dependents

3420 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import ViewResolver from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5
6access(all) contract DimensionX: NonFungibleToken {
7
8    access(all) var totalSupply: UInt64
9    access(all) var customSupply: UInt64
10    access(all) var genesisSupply: UInt64
11    access(all) var commonSupply: UInt64
12
13    access(all) var totalBurned: UInt64
14    access(all) var customBurned: UInt64
15    access(all) var genesisBurned: UInt64
16    access(all) var commonBurned: UInt64
17
18    access(all) var thulMintPrice: UFix64
19    access(all) var thulMintEnabled: Bool
20    access(all) var metadataUrl: String
21    access(all) var stakedNfts: {UInt64: Address} // map nftId -> ownerAddress
22
23    access(all) var crypthulhuAwake: UFix64
24    access(all) var crypthulhuSleepTime: UFix64
25    access(all) fun crypthulhuSleeps(): Bool {
26        return getCurrentBlock().timestamp - DimensionX.crypthulhuAwake > DimensionX.crypthulhuSleepTime
27    }
28
29    access(all) event ContractInitialized()
30    access(all) event Withdraw(id: UInt64, from: Address?)
31    access(all) event Deposit(id: UInt64, to: Address?)
32    access(all) event Mint(id: UInt64, type: UInt8)
33    access(all) event MintStore(id: UInt64, type: UInt8, setId: UInt32)
34    access(all) event Burn(id: UInt64, type: UInt8)
35    access(all) event Stake(id: UInt64, to: Address?)
36    access(all) event Unstake(id: UInt64, from: Address?)
37    access(all) event MinterCreated()
38
39    access(all) let CollectionStoragePath: StoragePath
40    access(all) let CollectionPublicPath: PublicPath
41    access(all) let AdminStoragePath: StoragePath
42    access(all) let MinterStoragePath: StoragePath
43
44    access(all) enum NFTType: UInt8 {
45        access(all) case custom
46        access(all) case genesis
47        access(all) case common
48    }
49
50    access(all) resource NFT: NonFungibleToken.NFT {
51
52        access(all) let id: UInt64
53        access(all) let type: NFTType
54
55        init(
56            id: UInt64,
57            type: NFTType,
58        ) {
59            self.id = id
60            self.type = type
61        }
62
63        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
64            return <-DimensionX.createEmptyCollection(nftType: Type<@DimensionX.NFT>())
65        }
66
67        access(all) view fun getViews(): [Type] {
68            return [
69                Type<MetadataViews.ExternalURL>(),
70                Type<MetadataViews.Display>(),
71                Type<MetadataViews.Royalties>(),
72                Type<MetadataViews.NFTCollectionData>(),
73                Type<MetadataViews.NFTCollectionDisplay>(),
74                Type<MetadataViews.Serial>(),
75                Type<MetadataViews.Traits>()
76            ]
77        }
78
79        access(all) fun resolveView(_ view: Type): AnyStruct? {
80            switch view {
81                case Type<MetadataViews.ExternalURL>():
82                    return MetadataViews.ExternalURL(
83                        DimensionX.metadataUrl.concat("heroes/").concat(self.id.toString())
84                    )
85                case Type<MetadataViews.Display>():
86                    return MetadataViews.Display(
87                        name: ("DimensionX #").concat(self.id.toString()),
88                        description: "A Superhero capable of doing battle in the DimensionX Game!",
89                        thumbnail: MetadataViews.HTTPFile(
90                            url: DimensionX.metadataUrl.concat("heroes/i/").concat(self.id.toString()).concat(".png")
91                        )
92                    )
93                case Type<MetadataViews.Royalties>():
94                    let royalties : [MetadataViews.Royalty] = []
95                    royalties.append(MetadataViews.Royalty(
96                        receiver: DimensionX.account.capabilities.get<&{FungibleToken.Receiver}>(MetadataViews.getRoyaltyReceiverPublicPath()),
97                        cut: 0.10,
98                        description: "Crypthulhu royalties"))
99                    return MetadataViews.Royalties(royalties)
100                case Type<MetadataViews.NFTCollectionData>():
101                    return DimensionX.resolveContractView(resourceType: Type<@DimensionX.NFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
102                case Type<MetadataViews.NFTCollectionDisplay>():
103                    return DimensionX.resolveContractView(resourceType: Type<@DimensionX.NFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>())
104                case Type<MetadataViews.Serial>():
105                    return MetadataViews.Serial(
106                        self.id
107                    )
108                case Type<MetadataViews.Traits>(): 
109                    return MetadataViews.Traits([
110                        MetadataViews.Trait(
111                            name: "Staked",
112                            value: DimensionX.stakedNfts[self.uuid] != nil,
113                            displayType: nil,
114                            rarity: nil
115                        )
116                    ])  
117            }
118
119            return nil
120        }
121    }
122
123    access(all) resource interface CollectionPublic {}
124
125    access(all) resource Collection: NonFungibleToken.Collection, CollectionPublic {
126        // dictionary of NFT conforming tokens
127        // NFT is a resource type with an `UInt64` ID field
128        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
129
130        init () {
131            self.ownedNFTs <- {}
132        }
133
134        access(NonFungibleToken.Withdraw) fun stake(id: UInt64) {
135            pre {
136                self.ownedNFTs.containsKey(id):
137                    "Cannot stake: you can only stake tokens that you own"
138                !DimensionX.stakedNfts.containsKey(id):
139                    "Cannot stake: the token is already staked"
140            }
141
142            let ownerAddress = self.owner?.address
143            DimensionX.stakedNfts[id] = ownerAddress
144
145            emit Stake(id: id, to:ownerAddress)
146        }
147
148        access(NonFungibleToken.Withdraw) fun unstake(id: UInt64) {
149            pre {
150                DimensionX.stakedNfts.containsKey(id):
151                    "Cannot unstake: the token is not staked"
152                self.ownedNFTs.containsKey(id):
153                    "Cannot unstake: you can only unstake tokens that you own"
154            }
155
156            if !DimensionX.crypthulhuSleeps() {
157                panic("Cannot unstake: you can only unstake through the game at this moment")
158            }
159
160            let ownerAddress = self.owner?.address
161            DimensionX.stakedNfts.remove(key: id)
162
163            emit Unstake(id: id, from: ownerAddress)
164        }
165
166        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
167            let supportedTypes: {Type: Bool} = {}
168            supportedTypes[Type<@DimensionX.NFT>()] = true
169            return supportedTypes
170        }
171
172        access(all) view fun isSupportedNFTType(type: Type): Bool {
173            return type == Type<@DimensionX.NFT>()
174        }
175
176        // withdraw removes an NFT from the collection and moves it to the caller
177        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
178            pre {
179                !DimensionX.stakedNfts.containsKey(withdrawID):
180                    "Cannot withdraw: the token is staked"
181            }
182
183            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
184
185            emit Withdraw(id: token.id, from: self.owner?.address)
186
187            return <-token
188        }
189
190        // deposit takes a NFT and adds it to the collections dictionary
191        // and adds the ID to the id array
192        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
193            let token <- token as! @DimensionX.NFT
194
195            let id: UInt64 = token.id
196
197            // add the new token to the dictionary which removes the old one
198            let oldToken <- self.ownedNFTs[id] <- token
199
200            emit Deposit(id: id, to: self.owner?.address)
201
202            destroy oldToken
203        }
204
205        // getIDs returns an array of the IDs that are in the collection
206        access(all) view fun getIDs(): [UInt64] {
207            return self.ownedNFTs.keys
208        }
209
210        // borrowNFT gets a reference to an NFT in the collection
211        // so that the caller can read its metadata and call its methods
212        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
213            return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
214        }
215 
216        // access(all) fun borrowDimensionX(id: UInt64): &DimensionX.NFT? {
217        //     if self.ownedNFTs[id] != nil {
218        //         // Create an authorized reference to allow downcasting
219        //         let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
220        //         return ref as! &DimensionX.NFT
221        //     }
222
223        //     return nil
224        // }
225
226        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
227            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
228                return nft as &{ViewResolver.Resolver}
229            }
230            return nil
231        }
232
233        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
234            return <-DimensionX.createEmptyCollection(nftType: Type<@DimensionX.NFT>())
235        }
236    }
237
238    // public function that anyone can call to create a new empty collection
239    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
240        return <- create Collection()
241    }
242
243    access(all) view fun getContractViews(resourceType: Type?): [Type] {
244        return [
245            Type<MetadataViews.NFTCollectionData>(),
246            Type<MetadataViews.NFTCollectionDisplay>()
247        ]
248    }
249
250    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
251        switch viewType {
252            case Type<MetadataViews.NFTCollectionData>():
253                return MetadataViews.NFTCollectionData(
254                    storagePath: DimensionX.CollectionStoragePath,
255                    publicPath: DimensionX.CollectionPublicPath,
256                    publicCollection: Type<&DimensionX.Collection>(),
257                    publicLinkedType: Type<&DimensionX.Collection>(),
258                    createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
259                        return <-DimensionX.createEmptyCollection(nftType: Type<@DimensionX.NFT>())
260                    })
261                )
262            case Type<MetadataViews.NFTCollectionDisplay>():
263                return MetadataViews.NFTCollectionDisplay(
264                    name: "Dimension X",
265                    description: "Dimension X is a Free-to-Play, Play-to-Earn strategic role playing game on the Flow blockchain set in the Dimension X comic book universe, where a pan-dimensional explosion created super powered humans, aliens and monsters with radical and terrifying superpowers!",
266                    externalURL: MetadataViews.ExternalURL("https://dimensionxnft.com"),
267                    squareImage: MetadataViews.Media(
268                        file: MetadataViews.HTTPFile(url: DimensionX.metadataUrl.concat("heroes/collection_image.png")),
269                        mediaType: "image/png"
270                    ),
271                    bannerImage: MetadataViews.Media(
272                        file: MetadataViews.HTTPFile(url: DimensionX.metadataUrl.concat("heroes/collection_banner.png")),
273                        mediaType: "image/png"
274                    ),
275                    socials: {
276                        "discord": MetadataViews.ExternalURL("https://discord.gg/dimensionx"),
277                        "twitter": MetadataViews.ExternalURL("https://twitter.com/DimensionX_NFT")
278                    }
279                )
280        }
281        return nil
282    }
283
284    // Resource that an admin or something similar would own to be
285    // able to mint new NFTs
286    //
287    access(all) resource NFTMinter {
288        // range if possible
289        access(all) fun getNextCustomID(): UInt64 {
290            var nextId = DimensionX.customSupply + 1
291            return (nextId <= 1000) ? nextId : self.getNextCommonID()
292        }
293
294        // Determine the next available ID for genesis NFTs and use the reserved
295        // range if possible
296        access(all) fun getNextGenesisID(): UInt64 {
297            var nextId = UInt64(1000) + DimensionX.genesisSupply + UInt64(1)
298            return (nextId <= 11000) ? nextId : panic("Cannot mint more than 10000 genesis NFTs")
299        }
300
301        // Determine the next available ID for the rest of NFTs and take into
302        // account the custom NFTs that have been minted outside of the reserved
303        // range
304        access(all) fun getNextCommonID(): UInt64 {
305            var customIdOverflow = Int256(DimensionX.customSupply) - Int256(1000)
306            customIdOverflow = customIdOverflow > 0 ? customIdOverflow : 0
307            return 11000 + DimensionX.commonSupply + UInt64(customIdOverflow) + UInt64(1)
308        }
309
310        access(all) fun mintCustomNFT(
311            recipient: &DimensionX.Collection,
312        ) {
313            var nextId = self.getNextCustomID()
314
315            // Update supply counters
316            DimensionX.customSupply = DimensionX.customSupply + UInt64(1)
317            DimensionX.totalSupply = DimensionX.totalSupply + UInt64(1)
318
319            self.mint(
320                recipient: recipient,
321                id: nextId,
322                type: DimensionX.NFTType.custom
323            )
324        }
325
326        access(all) fun mintGenesisNFT(
327            recipient: &DimensionX.Collection,
328        ) {
329            // Determine the next available ID
330            var nextId = self.getNextGenesisID()
331
332            // Update supply counters
333            DimensionX.genesisSupply = DimensionX.genesisSupply + UInt64(1)
334            DimensionX.totalSupply = DimensionX.totalSupply + UInt64(1)
335
336            self.mint(
337                recipient: recipient,
338                id: nextId,
339                type: DimensionX.NFTType.genesis
340            )
341        }
342
343        access(all) fun mintNFT(
344            recipient: &DimensionX.Collection,
345        ) {
346            // Determine the next available ID
347            var nextId = self.getNextCommonID()
348
349            // Update supply counters
350            DimensionX.commonSupply = DimensionX.commonSupply + UInt64(1)
351            DimensionX.totalSupply = DimensionX.totalSupply + UInt64(1)
352
353            self.mint(
354                recipient: recipient,
355                id: nextId,
356                type: DimensionX.NFTType.common
357            )
358        }
359
360        access(all) fun mintStoreNFT(
361            recipient: &DimensionX.Collection,
362            setId: UInt32,
363        ) {
364            // Determine the next available ID
365            var nextId = self.getNextCommonID()
366
367            // Update supply counters
368            DimensionX.commonSupply = DimensionX.commonSupply + UInt64(1)
369            DimensionX.totalSupply = DimensionX.totalSupply + UInt64(1)
370
371            var newNFT <- create NFT(id: nextId, type: NFTType.common)
372            emit MintStore(id: nextId, type: UInt8(3), setId: setId)
373            recipient.deposit(token: <-newNFT)
374
375            let ownerAddress = recipient.owner?.address
376            DimensionX.stakedNfts[nextId] = ownerAddress
377            emit Stake(id: nextId, to:ownerAddress)
378        }
379
380        access(all) fun mintStakedNFT(
381            recipient: &DimensionX.Collection,
382        ) {
383            var nextId = self.getNextCommonID()
384            self.mintNFT(recipient: recipient)
385            let ownerAddress = recipient.owner?.address
386            DimensionX.stakedNfts[nextId] = ownerAddress
387            emit Stake(id: nextId, to:ownerAddress)
388        }
389
390        access(self) fun mint(
391            recipient: &DimensionX.Collection,
392            id: UInt64,
393            type: DimensionX.NFTType,
394        ) {
395            // create a new NFT
396            var newNFT <- create NFT(id: id, type: type)
397            switch newNFT.type {
398                case NFTType.custom:
399                    emit Mint(id: id, type: 0)
400                case NFTType.genesis:
401                    emit Mint(id: id, type: 1)
402                case NFTType.common:
403                    emit Mint(id: id, type: 2)
404            }
405            // deposit it in the recipient's account using their reference
406            recipient.deposit(token: <-newNFT)
407        }
408    }
409
410    access(all) resource Admin {
411        access(all) fun unstake(id: UInt64) {
412            pre {
413                DimensionX.stakedNfts.containsKey(id):
414                    "Cannot unstake: the token is not staked"
415            }
416
417            let ownerAddress = DimensionX.stakedNfts[id]
418            DimensionX.stakedNfts.remove(key: id)
419
420            emit Unstake(id: id, from: ownerAddress)
421        }
422
423        access(all) fun setMetadataUrl(url: String) {
424            DimensionX.metadataUrl = url
425        }
426
427        access(all) fun createNFTMinter(): @NFTMinter {
428            emit MinterCreated()
429            return <-create NFTMinter()
430        }
431
432        access(all) fun setCrypthulhuSleepTime(time: UFix64) {
433            DimensionX.crypthulhuSleepTime = time
434            self.crypthulhuAwake()
435        }
436
437        access(all) fun crypthulhuAwake() {
438            DimensionX.crypthulhuAwake = getCurrentBlock().timestamp
439        }
440    }
441
442    init() {
443        // Initialize supply counters
444        self.totalSupply = 0
445        self.customSupply = 0
446        self.genesisSupply = 0
447        self.commonSupply = 0
448
449        // Initialize burned counters
450        self.totalBurned = 0
451        self.customBurned = 0
452        self.genesisBurned = 0
453        self.commonBurned = 0
454
455        self.thulMintPrice = 120.0
456        self.thulMintEnabled = false
457        self.metadataUrl = "https://www.dimensionx.com/api/nfts/"
458        self.stakedNfts = {}
459
460        // Initialize Dead Man's Switch
461        self.crypthulhuAwake = getCurrentBlock().timestamp
462        self.crypthulhuSleepTime = UFix64(60 * 60 * 24 * 30)
463
464        // Set the named paths
465        self.CollectionStoragePath = /storage/dmxCollection
466        self.CollectionPublicPath = /public/dmxCollection
467        self.AdminStoragePath = /storage/dmxAdmin
468        self.MinterStoragePath = /storage/dmxMinter
469
470        // Create a Collection resource and save it to storage
471        let collection <- create Collection()
472        self.account.storage.save(<-collection, to: self.CollectionStoragePath)
473
474        // create a public capability for the collection
475        let collectionCap = self.account.capabilities.storage.issue<&DimensionX.Collection>(self.CollectionStoragePath)
476        self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
477
478        let admin <- create Admin()
479        let minter <- admin.createNFTMinter()
480        self.account.storage.save(<-admin, to: self.AdminStoragePath)
481        self.account.storage.save(<-minter, to: self.MinterStoragePath)
482
483        emit ContractInitialized()
484    }
485}
486