Smart Contract
MLS
A.4cf4c4ee474ac04b.MLS
1// Description: Smart Contract for MLS Virtual Commemorative Tickets
2// SPDX-License-Identifier: UNLICENSED
3
4import NonFungibleToken from 0x1d7e57aa55817448
5import MetadataViews from 0x1d7e57aa55817448
6import ViewResolver from 0x1d7e57aa55817448
7
8access(all) contract MLS : NonFungibleToken {
9
10 access(all) var totalSupply: UInt64
11
12 access(all) event ContractInitialized()
13 access(all) event Withdraw(id: UInt64, from: Address?)
14 access(all) event Deposit(id: UInt64, to: Address?)
15
16 access(all) let CollectionStoragePath: StoragePath
17 access(all) let CollectionPublicPath: PublicPath
18 access(all) let MinterStoragePath: StoragePath
19
20 // Correct type from NonFungibleToken.INFT to NonFungibleToken.NFT
21 access(all) resource NFT : NonFungibleToken.NFT {
22 access(all) let id: UInt64
23 access(all) var link: String
24 access(all) var batch: UInt32
25 access(all) var sequence: UInt16
26 access(all) var limit: UInt16
27
28 init(
29 initID: UInt64,
30 initlink: String,
31 initbatch: UInt32,
32 initsequence: UInt16,
33 initlimit: UInt16
34 ) {
35 self.id = initID
36 self.link = initlink
37 self.batch = initbatch
38 self.sequence = initsequence
39 self.limit = initlimit
40 }
41
42 /// createEmptyCollection creates an empty Collection
43 /// and returns it to the caller so that they can own NFTs
44 /// @{NonFungibleToken.Collection}
45 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
46 return <-MLS.createEmptyCollection(nftType: Type<@MLS.NFT>())
47 }
48
49 access(all) view fun getViews(): [Type] {
50 return [
51 Type<MetadataViews.NFTCollectionData>()
52 ]
53 }
54
55 access(all) fun resolveView(_ view: Type): AnyStruct? {
56 switch view {
57 case Type<MetadataViews.NFTCollectionData>():
58 return MLS.resolveContractView(resourceType: Type<@MLS.NFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
59 }
60 return nil
61 }
62 }
63
64 // Ensure all type specifications conform to interface requirements
65 access(all) resource interface MLSCollectionPublic {
66 // Deprecated, only here for backwards compatibility
67 }
68
69 access(all) resource Collection : NonFungibleToken.Collection, MLSCollectionPublic {
70 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
71
72 init() {
73 self.ownedNFTs <- {}
74 }
75
76 /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
77 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
78 let supportedTypes: {Type: Bool} = {}
79 supportedTypes[Type<@MLS.NFT>()] = true
80 return supportedTypes
81 }
82
83 /// Returns whether or not the given type is accepted by the collection
84 /// A collection that can accept any type should just return true by default
85 access(all) view fun isSupportedNFTType(type: Type): Bool {
86 return type == Type<@MLS.NFT>()
87 }
88
89 /// withdraw removes an NFT from the collection and moves it to the caller
90 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
91 let token <- self.ownedNFTs.remove(key: withdrawID)
92 ?? panic("Could not withdraw an NFT with the provided ID from the collection")
93
94 return <-token
95 }
96
97 /// deposit takes a NFT and adds it to the collections dictionary
98 /// and adds the ID to the id array
99 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
100 let token <- token as! @MLS.NFT
101 let id = token.id
102
103 // add the new token to the dictionary which removes the old one
104 let oldToken <- self.ownedNFTs[token.id] <- token
105
106 destroy oldToken
107
108 // This code is for testing purposes only
109 // Do not add to your contract unless you have a specific
110 // reason to want to emit the NFTUpdated event somewhere
111 // in your contract
112 let authTokenRef = (&self.ownedNFTs[id] as auth(NonFungibleToken.Update) &{NonFungibleToken.NFT}?)!
113 //authTokenRef.updateTransferDate(date: getCurrentBlock().timestamp)
114 MLS.emitNFTUpdated(authTokenRef)
115 }
116
117 /// getIDs returns an array of the IDs that are in the collection
118 access(all) view fun getIDs(): [UInt64] {
119 return self.ownedNFTs.keys
120 }
121
122 /// Gets the amount of NFTs stored in the collection
123 access(all) view fun getLength(): Int {
124 return self.ownedNFTs.length
125 }
126
127 /// Borrow the view resolver for the specified NFT ID
128 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
129 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
130 return nft as &{ViewResolver.Resolver}
131 }
132 return nil
133 }
134
135 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
136 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
137 }
138
139 /// createEmptyCollection creates an empty Collection of the same type
140 /// and returns it to the caller
141 /// @return A an empty collection of the same type
142 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
143 return <- MLS.createEmptyCollection(nftType: Type<@MLS.NFT>())
144 }
145
146 }
147
148 /// createEmptyCollection creates an empty Collection for the specified NFT type
149 /// and returns it to the caller so that they can own NFTs
150 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
151 return <- create Collection()
152 }
153
154 /// Function that returns all the Metadata Views implemented by a Non Fungible Token
155 ///
156 /// @return An array of Types defining the implemented views. This value will be used by
157 /// developers to know which parameter to pass to the resolveView() method.
158 ///
159 access(all) view fun getContractViews(resourceType: Type?): [Type] {
160 return [
161 Type<MetadataViews.NFTCollectionData>(),
162 Type<MetadataViews.NFTCollectionDisplay>(),
163 Type<MetadataViews.EVMBridgedMetadata>()
164 ]
165 }
166
167 /// Function that resolves a metadata view for this contract.
168 ///
169 /// @param view: The Type of the desired view.
170 /// @return A structure representing the requested view.
171 ///
172 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
173 switch viewType {
174 case Type<MetadataViews.NFTCollectionData>():
175 let collectionData = MetadataViews.NFTCollectionData(
176 storagePath: self.CollectionStoragePath,
177 publicPath: self.CollectionPublicPath,
178 publicCollection: Type<&MLS.Collection>(),
179 publicLinkedType: Type<&MLS.Collection>(),
180 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
181 return <-MLS.createEmptyCollection(nftType: Type<@MLS.NFT>())
182 })
183 )
184 return collectionData
185 case Type<MetadataViews.NFTCollectionDisplay>():
186 let media = MetadataViews.Media(
187 file: MetadataViews.HTTPFile(
188 url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
189 ),
190 mediaType: "image/svg+xml"
191 )
192 return MetadataViews.NFTCollectionDisplay(
193 name: "MLS Collection",
194 description: "This collection represents MLS Virtual Commemorative Tickets.",
195 externalURL: MetadataViews.ExternalURL("https://mls-nft.onflow.org"),
196 squareImage: media,
197 bannerImage: media,
198 socials: {
199 "twitter": MetadataViews.ExternalURL("https://twitter.com/mls")
200 }
201 )
202 case Type<MetadataViews.EVMBridgedMetadata>():
203 // Implementing this view gives the project control over how the bridged NFT is represented as an ERC721
204 // when bridged to EVM on Flow via the public infrastructure bridge.
205
206 // Compose the contract-level URI. In this case, the contract metadata is located on some HTTP host,
207 // but it could be IPFS, S3, a data URL containing the JSON directly, etc.
208 return MetadataViews.EVMBridgedMetadata(
209 name: "MLS",
210 symbol: "MLS",
211 uri: MetadataViews.URI(
212 baseURI: nil, // setting baseURI as nil sets the given value as the uri field value
213 value: "https://mls-nft.onflow.org/contract-metadata.json"
214 )
215 )
216 }
217 return nil
218 }
219
220 /// Resource that an admin or something similar would own to be
221 /// able to mint new NFTs
222 ///
223 access(all) resource NFTMinter {
224
225 /// mintNFT mints a new NFT with a new ID
226 /// and returns it to the calling context
227 access(all) fun mintNFT(
228 glink: String,
229 gbatch: UInt32,
230 glimit: UInt16,
231 gsequence: UInt16
232 ): @MLS.NFT {
233
234 let tokenID = (UInt64(gbatch) << 32) | (UInt64(glimit) << 16) | UInt64(gsequence)
235
236 let metadata: {String: AnyStruct} = {}
237 let currentBlock = getCurrentBlock()
238 metadata["mintedBlock"] = currentBlock.height
239 metadata["mintedTime"] = currentBlock.timestamp
240
241 // this piece of metadata will be used to show embedding rarity into a trait
242 metadata["foo"] = "bar"
243
244 // create a new NFT
245 var newNFT <- create NFT(
246 initID: tokenID,
247 initlink: glink,
248 initbatch: gbatch,
249 initsequence: gsequence,
250 initlimit: glimit
251 )
252 MLS.totalSupply = MLS.totalSupply + 1
253 return <-newNFT
254 }
255 }
256
257 init() {
258 self.CollectionStoragePath = /storage/MLSCollection
259 self.CollectionPublicPath = /public/MLSCollection
260 self.MinterStoragePath = /storage/MLSMinter
261 self.totalSupply = 0
262
263 let collection <- create Collection()
264 self.account.storage.save(<-collection, to: self.CollectionStoragePath)
265
266 let collectionCap = self.account.capabilities.storage.issue<&MLS.Collection>(self.CollectionStoragePath)
267 self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
268
269 let minter <- create NFTMinter()
270 self.account.storage.save(<-minter, to: self.MinterStoragePath)
271 }
272}
273