Smart Contract
Patch
A.807c3d470888cc48.Patch
1// SPDX-License-Identifier: UNLICENSED
2import NonFungibleToken from 0x1d7e57aa55817448
3import ViewResolver from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5
6access(all)
7contract Patch: NonFungibleToken{
8 access(all)
9 event ContractInitialized()
10
11 access(all)
12 event NFTTemplateCreated(templateID: UInt64, template: Patch.PatchTemplate)
13
14 access(all)
15 event Withdraw(id: UInt64, from: Address?)
16
17 access(all)
18 event Deposit(id: UInt64, to: Address?)
19
20 access(all)
21 event Mint(id: UInt64, templateID: UInt64, serialNumber: UInt64)
22
23 access(all)
24 event Burn(id: UInt64)
25
26 access(all)
27 event TemplateUpdated(template: PatchTemplate)
28
29 access(all)
30 let CollectionStoragePath: StoragePath
31
32 access(all)
33 let CollectionPublicPath: PublicPath
34
35 access(all)
36 let AdminStoragePath: StoragePath
37
38 access(all)
39 var totalSupply: UInt64
40
41 access(all)
42 var nextTemplateID: UInt64
43
44 access(self)
45 var PatchTemplates:{ UInt64: PatchTemplate}
46
47 access(self)
48 var tokenMintedPerType:{ UInt64: UInt64}
49
50 access(all)
51 resource interface PatchCollectionPublic{
52 access(all)
53 fun deposit(token: @{NonFungibleToken.NFT}): Void
54
55 access(all)
56 view fun getIDs(): [UInt64]
57
58 access(all)
59 view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
60
61 access(all)
62 fun borrowPatch(id: UInt64): &Patch.NFT?{
63 post{
64 result == nil || result?.id == id:
65 "Cannot borrow Patch reference: The ID of the returned reference is incorrect"
66 }
67 }
68 }
69
70 access(all)
71 struct PatchTemplate{
72 access(all)
73 let templateID: UInt64
74
75 access(all)
76 var name: String
77
78 access(all)
79 var description: String
80
81 access(all)
82 var mintLimit: UInt64
83
84 access(all)
85 var locked: Bool
86
87 access(all)
88 var nextSerialNumber: UInt64
89
90 access(self)
91 var metadata:{ String: String}
92
93 access(all)
94 fun getMetadata():{ String: String}{
95 return self.metadata
96 }
97
98 access(all)
99 fun incrementSerialNumber(){
100 self.nextSerialNumber = self.nextSerialNumber + 1
101 }
102
103 access(all)
104 fun lockTemplate(){
105 self.locked = true
106 }
107
108 access(all)
109 fun updateMetadata(newMetadata:{ String: String}){
110 pre{
111 newMetadata.length != 0:
112 "New Template metadata cannot be empty"
113 }
114 self.metadata = newMetadata
115 }
116
117 init(templateID: UInt64, name: String, description: String, mintLimit: UInt64, metadata:{ String: String}){
118 pre{
119 metadata.length != 0:
120 "New Template metadata cannot be empty"
121 }
122 self.templateID = templateID
123 self.name = name
124 self.description = description
125 self.mintLimit = mintLimit
126 self.metadata = metadata
127 self.locked = false
128 self.nextSerialNumber = 1
129 Patch.nextTemplateID = Patch.nextTemplateID + 1
130 emit NFTTemplateCreated(templateID: self.templateID, template: self)
131 }
132 }
133
134 access(all)
135 resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver{
136 access(all)
137 let id: UInt64
138
139 access(all)
140 let templateID: UInt64
141
142 access(all)
143 let serialNumber: UInt64
144
145 access(all)
146 view fun getViews(): [Type]{
147 return [Type<MetadataViews.Display>(), Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.ExternalURL>(), Type<MetadataViews.Royalties>()]
148 }
149
150 access(all)
151 fun resolveView(_ view: Type): AnyStruct?{
152 switch view{
153 case Type<MetadataViews.Display>():
154 return MetadataViews.Display(name: self.getNFTTemplate().name, description: self.getNFTTemplate().description, thumbnail: MetadataViews.HTTPFile(url: self.getNFTTemplate().getMetadata()["uri"]!))
155 case Type<MetadataViews.ExternalURL>():
156 return MetadataViews.ExternalURL("https://flunks.io/")
157 case Type<MetadataViews.NFTCollectionData>():
158 return MetadataViews.NFTCollectionData(storagePath: Patch.CollectionStoragePath, publicPath: Patch.CollectionPublicPath, publicCollection: Type<&Patch.Collection>(), publicLinkedType: Type<&Patch.Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{
159 return <-Patch.createEmptyCollection(nftType: Type<@Patch.Collection>())
160 })
161 case Type<MetadataViews.NFTCollectionDisplay>():
162 let media = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://storage.googleapis.com/flunks_public/website-assets/classroom.png"), mediaType: "image/png")
163 return MetadataViews.NFTCollectionDisplay(name: "Backpack Patch", description: "Backpack Patches #onFlow", externalURL: MetadataViews.ExternalURL("https://flunks.io/"), squareImage: media, bannerImage: media, socials:{ "twitter": MetadataViews.ExternalURL("https://twitter.com/flunks_nft")})
164 case Type<MetadataViews.Royalties>():
165 return MetadataViews.Royalties([])
166 }
167 return nil
168 }
169
170 access(all)
171 fun getNFTTemplate(): PatchTemplate{
172 return Patch.PatchTemplates[self.templateID]!
173 }
174
175 access(all)
176 fun getNFTMetadata():{ String: String}{
177 return (Patch.PatchTemplates[self.templateID]!).getMetadata()
178 }
179
180 access(all)
181 fun createEmptyCollection(): @{NonFungibleToken.Collection}{
182 return <-create Collection()
183 }
184
185 init(initID: UInt64, initTemplateID: UInt64, serialNumber: UInt64){
186 self.id = initID
187 self.templateID = initTemplateID
188 self.serialNumber = serialNumber
189 }
190 }
191
192 access(all)
193 resource Collection: PatchCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection{
194 access(all)
195 var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
196
197 access(NonFungibleToken.Withdraw)
198 fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{
199 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
200 emit Withdraw(id: token.id, from: self.owner?.address)
201 return <-token
202 }
203
204 access(all)
205 fun deposit(token: @{NonFungibleToken.NFT}): Void{
206 let token <- token as! @Patch.NFT
207 let id: UInt64 = token.id
208 let oldToken <- self.ownedNFTs[id] <- token
209 emit Deposit(id: id, to: self.owner?.address)
210 destroy oldToken
211 }
212
213 access(self)
214 fun batchDeposit(collection: @Collection){
215 let keys = collection.getIDs()
216 for key in keys{
217 self.deposit(token: <-collection.withdraw(withdrawID: key))
218 }
219 destroy collection
220 }
221
222 access(all)
223 view fun getIDs(): [UInt64]{
224 return self.ownedNFTs.keys
225 }
226
227 access(all)
228 view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{
229 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
230 }
231
232 access(all)
233 fun borrowPatch(id: UInt64): &Patch.NFT?{
234 if self.ownedNFTs[id] != nil{
235 let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
236 return ref as! &Patch.NFT
237 } else{
238 return nil
239 }
240 }
241
242 access(all)
243 view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?{
244 let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
245 let exampleNFT = nft as! &Patch.NFT
246 return exampleNFT as &{ViewResolver.Resolver}
247 }
248
249 access(all)
250 view fun getSupportedNFTTypes():{ Type: Bool}{
251 panic("implement me")
252 }
253
254 access(all)
255 view fun isSupportedNFTType(type: Type): Bool{
256 panic("implement me")
257 }
258
259 access(all)
260 fun createEmptyCollection(): @{NonFungibleToken.Collection}{
261 return <-create Collection()
262 }
263
264 init(){
265 self.ownedNFTs <-{}
266 }
267 }
268
269 access(all)
270 fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{
271 return <-create Collection()
272 }
273
274 access(all)
275 resource Admin{
276 access(self)
277 fun mintNFT(recipient: &{NonFungibleToken.CollectionPublic}, templateID: UInt64){
278 pre{
279 Patch.PatchTemplates[templateID] != nil:
280 "Template doesn't exist"
281 !(Patch.PatchTemplates[templateID]!).locked:
282 "Cannot mint Patch - template is locked"
283 (Patch.PatchTemplates[templateID]!).nextSerialNumber <= (Patch.PatchTemplates[templateID]!).mintLimit:
284 "Cannot mint Patch - mint limit reached"
285 }
286
287 // TODO: mint Patch NFT
288 let nftTemplate = Patch.PatchTemplates[templateID]!
289 let newNFT: @NFT <- create Patch.NFT(initID: Patch.totalSupply, initTemplateID: templateID, serialNumber: nftTemplate.nextSerialNumber)
290 emit Mint(id: newNFT.id, templateID: nftTemplate.templateID, serialNumber: nftTemplate.nextSerialNumber)
291 Patch.totalSupply = Patch.totalSupply + 1
292 (Patch.PatchTemplates[templateID]!).incrementSerialNumber()
293 recipient.deposit(token: <-newNFT)
294 }
295
296 access(self)
297 fun createPatchTemplate(name: String, description: String, mintLimit: UInt64, metadata:{ String: String}){
298 Patch.PatchTemplates[Patch.nextTemplateID] = PatchTemplate(templateID: Patch.nextTemplateID, name: name, description: description, mintLimit: mintLimit, metadata: metadata)
299 }
300
301 access(self)
302 fun updatePatchTemplate(templateID: UInt64, newMetadata:{ String: String}){
303 pre{
304 Patch.PatchTemplates.containsKey(templateID) != nil:
305 "Template does not exits."
306 }
307 (Patch.PatchTemplates[templateID]!).updateMetadata(newMetadata: newMetadata)
308 }
309 }
310
311 access(self)
312 fun getPatchTemplateByID(templateID: UInt64): Patch.PatchTemplate{
313 return Patch.PatchTemplates[templateID]!
314 }
315
316 access(self)
317 fun getPatchTemplates():{ UInt64: Patch.PatchTemplate}{
318 return Patch.PatchTemplates
319 }
320
321 access(all) view fun getContractViews(resourceType: Type?): [Type] {
322 return [
323 Type<MetadataViews.NFTCollectionData>(),
324 Type<MetadataViews.NFTCollectionDisplay>(),
325 Type<MetadataViews.EVMBridgedMetadata>()
326 ]
327 }
328
329 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
330 return nil
331 }
332
333 init(){
334 self.CollectionStoragePath = /storage/PatchCollection
335 self.CollectionPublicPath = /public/PatchCollection
336 self.AdminStoragePath = /storage/PatchAdmin
337 self.totalSupply = 0
338 self.nextTemplateID = 1
339 self.tokenMintedPerType ={}
340 self.PatchTemplates ={}
341 let admin <- create Admin()
342 self.account.storage.save(<-admin, to: self.AdminStoragePath)
343 emit ContractInitialized()
344 }
345}
346