Smart Contract

HotspotOperatorNFT

A.efc9bea2fda54f34.HotspotOperatorNFT

Deployed

1w ago
Feb 19, 2026, 10:38:14 PM UTC

Dependents

7 imports
1// HotspotOperatorNFT.cdc
2//
3// This contract implements a non-fungible token (NFT) for representing Hotspot Operator NFTs.
4// It allows users to register hotspots and receive coverage rewards.
5
6import NonFungibleToken from 0x1d7e57aa55817448
7import ViewResolver from 0x1d7e57aa55817448
8import MetadataViews from 0x1d7e57aa55817448
9import FIVEGCOIN from 0xefc9bea2fda54f34
10import RandomPicker from 0x85ba07db77467695
11import CrossVMMetadataViews from 0x1d7e57aa55817448
12import EVM from 0xe467b9dd11fa00df
13access(all) contract HotspotOperatorNFT: NonFungibleToken, ViewResolver {
14
15    // Events
16    access(all) event ContractInitialized()
17    access(all) event Withdraw(id: UInt64, from: Address?)
18    access(all) event Deposit(id: UInt64, to: Address?)
19    access(all) event Minted(id: UInt64, recipient: Address?)
20
21    // Named paths
22    access(all) let CollectionStoragePath: StoragePath
23    access(all) let CollectionPublicPath: PublicPath
24
25    // Total supply of HotspotOperatorNFTs in existence
26    access(all) var totalSupply: UInt64
27    access(all) var minters: [Address]
28    access(all) var thumbnails: [String]
29
30    // Represents the metadata for a HotspotOperator NFT
31    access(all) struct HotspotOperatorData {
32        access(all) let name: String
33        access(all) let description: String
34        access(all) let thumbnail: String
35        access(all) let serial: UInt64
36        access(all) let createdAt: UFix64
37
38        init(name: String, description: String, thumbnail: String, serial: UInt64) {
39            self.name = name
40            self.description = description
41            self.thumbnail = thumbnail
42            self.serial = serial
43            self.createdAt = getCurrentBlock().timestamp
44        }
45    }
46
47    //------------------------------------------------------------
48    // NFT Resource
49    //------------------------------------------------------------
50    // The NFT resource that represents ownership rights to a hotspot
51    access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
52        access(all) let id: UInt64
53        access(all) var metadata: HotspotOperatorData
54        access(all) var rewardsVault: @FIVEGCOIN.Vault
55
56        init(thumbnail: String) {
57
58            let metadata: HotspotOperatorData = HotspotOperatorData(
59                name: "Waddle 5G Operator",
60                description: "A 🎶 smooth operator 🎶 that represents ownership rights to a hotspot",
61                thumbnail: thumbnail,
62                serial: HotspotOperatorNFT.totalSupply
63            )
64
65            self.id = self.uuid
66            self.metadata = metadata
67            self.rewardsVault <- FIVEGCOIN.createEmptyVault(vaultType: Type<@FIVEGCOIN.Vault>())
68        }
69
70        access(all) view fun getViews(): [Type] {
71            return [
72                Type<MetadataViews.Display>(),
73				Type<MetadataViews.NFTCollectionDisplay>(),
74				Type<MetadataViews.NFTCollectionData>(),
75                Type<CrossVMMetadataViews.EVMPointer>(),
76                // Type<MetadataViews.EVMBridgedMetadata>(),
77				Type<MetadataViews.ExternalURL>(),
78				Type<MetadataViews.Traits>(),
79				Type<MetadataViews.Serial>()
80            ]
81        }
82
83        access(all) fun resolveView(_ view: Type): AnyStruct? {
84            switch view {
85                case Type<MetadataViews.Display>():
86                    return MetadataViews.Display(
87                        name: self.metadata.name.concat(" #").concat(self.metadata.serial.toString()), 
88                        description: self.metadata.description, 
89                        thumbnail: MetadataViews.HTTPFile(url: self.metadata.thumbnail)
90                        )
91                case Type<MetadataViews.NFTCollectionDisplay>():
92                    return HotspotOperatorNFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionDisplay>())
93                case Type<MetadataViews.NFTCollectionData>():
94                    return HotspotOperatorNFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>())
95                case Type<CrossVMMetadataViews.EVMPointer>():
96                    return HotspotOperatorNFT.resolveContractView(resourceType: nil, viewType: Type<CrossVMMetadataViews.EVMPointer>())
97                // case Type<MetadataViews.EVMBridgedMetadata>():
98                //     return MetadataViews.EVMBridgedMetadata(
99                //         name: "Waddle 5G Operators",
100                //         symbol: "WADDLE",
101                //         uri: MetadataViews.URI(
102                //             baseURI: "https://metadata-api.production.studio-platform.dapperlabs.com/v1/topshot/moment/", // TODO
103                //             value: self.id.toString()
104                //         )
105                //     )
106                case Type<MetadataViews.ExternalURL>():
107                    return MetadataViews.ExternalURL("https://waddle-git-main-basicbeasts.vercel.app/")
108				case Type<MetadataViews.Serial>():
109                    return MetadataViews.Serial(
110                        self.id
111                    )
112            }
113            return nil
114        }
115
116        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
117            return <-HotspotOperatorNFT.createEmptyCollection(nftType: Type<@NFT>())
118        }
119    }
120
121    // Interface that users can cast their Collection as to allow others to deposit HotspotOperatorNFTs
122    access(all) resource interface HotspotOperatorNFTCollectionPublic {
123        access(all) fun deposit(token: @{NonFungibleToken.NFT})
124        access(all) fun getIDs(): [UInt64]
125        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
126        access(all) fun borrowHotspotOperator(id: UInt64): &NFT? {
127            post {
128                (result == nil) || (result?.id == id): 
129                    "Cannot borrow HotspotOperator reference: The ID of the returned reference is incorrect"
130            }
131        }
132    }
133
134    //------------------------------------------------------------
135    // Collection resource that holds multiple HotspotOperator NFTs
136    //------------------------------------------------------------
137
138    access(all) resource Collection: HotspotOperatorNFTCollectionPublic, NonFungibleToken.Collection, ViewResolver.ResolverCollection {
139
140        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
141
142        init() {
143            self.ownedNFTs <- {}
144        }
145
146        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
147            let supportedTypes: {Type: Bool} = {}
148            supportedTypes[Type<@HotspotOperatorNFT.NFT>()] = true
149            return supportedTypes
150        }
151
152        access(all) view fun isSupportedNFTType(type: Type): Bool {
153            if type == Type<@HotspotOperatorNFT.NFT>() {
154                return true
155            }
156            return false
157        }
158
159        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
160            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
161
162            emit Withdraw(id: token.id, from: self.owner?.address)
163
164            return <-token
165        }
166
167        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
168            let token <- token as! @HotspotOperatorNFT.NFT
169            let id: UInt64 = token.id
170            
171            let oldToken <- self.ownedNFTs[id] <- token
172            
173            emit Deposit(id: id, to: self.owner?.address)
174            
175            destroy oldToken
176        }
177
178        access(all) view fun getIDs(): [UInt64] {
179            return self.ownedNFTs.keys
180        }
181
182        access(all) view fun getLength(): Int {
183            return self.ownedNFTs.keys.length
184        }
185
186        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
187            return &self.ownedNFTs[id]
188        }
189
190        access(all) fun borrowHotspotOperator(id: UInt64): &NFT? {
191            return self.borrowNFT(id) as! &NFT?
192        }
193
194        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
195            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
196                return nft as &{ViewResolver.Resolver}
197            }
198            return nil
199        }
200
201        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
202            return <-HotspotOperatorNFT.createEmptyCollection(nftType: Type<@NFT>())
203        }
204    }
205
206    //------------------------------------------------------------
207    // Contract Secure Onchain Random NFT Minting
208    //------------------------------------------------------------
209
210    access(all) fun mintNFTCommit(): @RandomPicker.Receipt {
211        var values: [UInt64] = []
212        var i: UInt64 = 0
213        while i < UInt64(HotspotOperatorNFT.thumbnails.length) {
214            values.append(i)
215            i = i + 1
216        }
217        return <-RandomPicker.commit(values: values)
218    }
219
220    access(all) fun mintNFTReveal(recipient: &{HotspotOperatorNFTCollectionPublic}, receipt: @RandomPicker.Receipt) {
221        
222        let randomIndex: UInt64 = RandomPicker.reveal(receipt: <-receipt)
223            
224        let newNFT: @HotspotOperatorNFT.NFT <- create NFT(thumbnail: HotspotOperatorNFT.thumbnails[randomIndex])
225
226        recipient.deposit(token: <-newNFT)
227
228        HotspotOperatorNFT.totalSupply = HotspotOperatorNFT.totalSupply + 1
229
230        emit Minted(id: HotspotOperatorNFT.totalSupply - 1, recipient: recipient.owner?.address)
231    }
232
233    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
234        return <- create Collection()
235    }
236
237    //------------------------------------------------------------
238    // Contract MetadataViews
239    //------------------------------------------------------------
240
241    access(all) view fun getContractViews(resourceType: Type?): [Type] {
242		return [
243			Type<MetadataViews.NFTCollectionDisplay>(),
244            Type<MetadataViews.NFTCollectionData>(),
245            Type<CrossVMMetadataViews.EVMPointer>()
246		]
247	}
248
249    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
250		switch viewType {
251			case Type<MetadataViews.NFTCollectionDisplay>():
252                let externalURL = MetadataViews.ExternalURL("https://waddle-git-main-basicbeasts.vercel.app/")
253                    let squareImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://raw.githubusercontent.com/bz-hashtag-0780/waddle/refs/heads/main/images/waddle_logo.png"), mediaType: "image/png")
254                    let bannerImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://raw.githubusercontent.com/bz-hashtag-0780/waddle/refs/heads/main/images/waddle_banner_1.png"), mediaType: "image/png")
255                    let socialMap:{ String: MetadataViews.ExternalURL} ={ "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain"), "discord": MetadataViews.ExternalURL("https://discord.com/invite/xgFtWhwSaR")}
256                    return MetadataViews.NFTCollectionDisplay(
257                        name: "Waddle 5G Operators",
258                        description: "An experimental NFT collection for Waddle 5G Operators",
259                        externalURL: externalURL,
260                        squareImage: squareImage,
261                        bannerImage: bannerImage,
262                        socials: socialMap
263                    )
264            case Type<MetadataViews.NFTCollectionData>():
265                return MetadataViews.NFTCollectionData(
266                    storagePath: /storage/HotspotOperatorNFTCollection_1,
267                    publicPath: /public/HotspotOperatorNFTCollection_1,
268                    publicCollection: Type<&HotspotOperatorNFT.Collection>(),
269                    publicLinkedType: Type<&HotspotOperatorNFT.Collection>(),
270                    createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
271                        return <- HotspotOperatorNFT.createEmptyCollection(nftType: Type<@NFT>())
272                    })
273                )
274            case Type<CrossVMMetadataViews.EVMPointer>():
275                return CrossVMMetadataViews.EVMPointer(
276                    cadenceType: Type<@HotspotOperatorNFT.NFT>(),
277                    cadenceContractAddress: self.account.address,
278                    evmContractAddress: EVM.addressFromString("0xf9780aa473942a68b4c4387fd256c179ccd132ae"),
279                    nativeVM: CrossVMMetadataViews.VM.Cadence
280                )
281		}
282		return nil
283	}
284
285    //------------------------------------------------------------
286    // Contract Initialization
287    //------------------------------------------------------------
288
289    init() {
290        self.totalSupply = 0
291        self.minters = []
292        self.thumbnails = [
293                "https://raw.githubusercontent.com/bz-hashtag-0780/waddle/refs/heads/main/images/waddle_operator_1.png",
294                "https://raw.githubusercontent.com/bz-hashtag-0780/waddle/refs/heads/main/images/waddle_operator_2.png",
295                "https://raw.githubusercontent.com/bz-hashtag-0780/waddle/refs/heads/main/images/waddle_operator_3.png",
296                "https://raw.githubusercontent.com/bz-hashtag-0780/waddle/refs/heads/main/images/waddle_operator_4.png",
297                "https://raw.githubusercontent.com/bz-hashtag-0780/waddle/refs/heads/main/images/waddle_operator_5.png",
298                "https://raw.githubusercontent.com/bz-hashtag-0780/waddle/refs/heads/main/images/waddle_operator_6.png"
299            ]
300        
301        self.CollectionStoragePath = /storage/HotspotOperatorNFTCollection_1
302        self.CollectionPublicPath = /public/HotspotOperatorNFTCollection_1
303
304        emit ContractInitialized()
305    }
306}