Smart Contract
Certifily
A.3c678a22cbfe5053.Certifily
1// Certifily NFT Smart Contract
2// NFT : www.Certifi.ly
3// Version : 1.0.0
4// Blockchain : Flow www.onFlow.org
5
6import NonFungibleToken from 0x1d7e57aa55817448
7import MetadataViews from 0x1d7e57aa55817448
8import ViewResolver from 0x1d7e57aa55817448
9
10access(all) contract Certifily: NonFungibleToken {
11
12 // Total number of token supply
13 access(all) var totalSupply: UInt64
14
15 /// Path where the `Collection` is stored
16 access(all) let certifilyStoragePath: StoragePath
17
18 /// Path where the public capability for the `Collection` is
19 access(all) let certifilyPublicPath: PublicPath
20
21 /// NFT Minter
22 access(all) let certifilyMinterPath: StoragePath
23
24 // Contract Events
25 access(all) event ContractInitialized()
26 access(all) event Withdraw(id: UInt64, from: Address?)
27 access(all) event Deposit(id: UInt64, to: Address?)
28 access(all) event Mint(id: UInt64, content:String, owner: Address?, insitution:String)
29
30 // TOKEN RESOURCE
31 access(all) resource NFT: NonFungibleToken.NFT {
32
33 // Unique identifier for NFT Token
34 access(all) let id :UInt64
35
36 // Meta data to store token data (use dict for data)
37 access(self) let metaData: {String : String}
38
39 access(all) fun getMetadata():{String: String} {
40 return self.metaData
41 }
42
43 // NFT token insitution
44 access(all) let insitution:String
45
46 access(all) let certType:UInt8
47
48 // Certificate token holder address
49 access(all) let holder:Address?
50
51 // View permission for the NFT (an array of addresses)
52 access(all) let viewPermission: [String]
53
54
55 // In current store static dict in meta data
56 init(id : UInt64, content : String, insitution:String, description:String , holder:Address?,previewContent:String,certType:UInt8, viewPermission: [String]) {
57 self.id = id
58 self.metaData = {"content" : content, "description": description, "previewContent":previewContent }
59 self.holder = holder
60 self.insitution = insitution
61 self.certType = certType
62 self.viewPermission = viewPermission
63 }
64
65 /// createEmptyCollection creates an empty Collection
66 /// and returns it to the caller so that they can own NFTs
67 /// @{NonFungibleToken.Collection}
68 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
69 return <-Certifily.createEmptyCollection(nftType: Type<@Certifily.NFT>())
70 }
71
72
73 access(all) view fun getViews(): [Type] {
74 return [
75 Type<MetadataViews.Display>(),
76 Type<MetadataViews.ExternalURL>(),
77 Type<MetadataViews.NFTCollectionData>(),
78 Type<MetadataViews.NFTCollectionDisplay>(),
79 Type<MetadataViews.Serial>(),
80 Type<MetadataViews.Traits>()
81 ]
82 }
83
84
85
86 // Function to add an address to viewPermission
87 access(all) fun addViewPermission(address: [String]) {
88
89 for addr in address {
90 // Check if the address is not already in the viewPermission array
91 if !self.viewPermission.contains(addr) {
92 self.viewPermission.append(addr)
93 }
94 }
95
96 }
97
98 // Function to remove an address from viewPermission
99 access(all) fun removeViewPermission(address: String) {
100
101 // Find the index of the address in viewPermission and remove it
102 let index = self.viewPermission.firstIndex(of: address)
103 if let idx = index {
104 self.viewPermission.remove(at: idx)
105 }
106
107 }
108
109 access(all) fun resolveView(_ view: Type): AnyStruct? {
110 switch view {
111 case Type<MetadataViews.Display>():
112 return MetadataViews.Display(
113 name: self.insitution,
114 description: self.metaData["description"]!,
115 thumbnail: MetadataViews.HTTPFile(
116 url: self.metaData["previewContent"]!
117 )
118 )
119 case Type<MetadataViews.Serial>():
120 return MetadataViews.Serial(
121 self.id
122 )
123 case Type<MetadataViews.ExternalURL>():
124 return MetadataViews.ExternalURL("https://certifi.ly")
125 case Type<MetadataViews.NFTCollectionData>():
126 return MetadataViews.NFTCollectionData(
127 storagePath: Certifily.certifilyStoragePath,
128 publicPath: Certifily.certifilyPublicPath,
129 publicCollection: Type<&Certifily.Collection>(),
130 publicLinkedType: Type<&Certifily.Collection>(),
131 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
132 return <-Certifily.createEmptyCollection(nftType: Type<@Certifily.NFT>())
133 })
134 )
135 case Type<MetadataViews.NFTCollectionDisplay>():
136 let media = MetadataViews.Media(
137 file: MetadataViews.HTTPFile(
138 url: "https://alpha.certifi.ly/logo512.png"
139 ),
140 mediaType: "image/png"
141 )
142 return MetadataViews.NFTCollectionDisplay(
143 name: "Certifily Collection",
144 description: "Next-gen NFT documents B2B SaaS platform for all-gen",
145 externalURL: MetadataViews.ExternalURL("https://certifi.ly"),
146 squareImage: media,
147 bannerImage: media,
148 socials: {
149 "twitter": MetadataViews.ExternalURL("https://twitter.com/certifily"),
150 "instagram": MetadataViews.ExternalURL("https://www.instagram.com/certifi.ly/"),
151 "discord" : MetadataViews.ExternalURL("https://discord.io/certifily")
152 }
153 )
154 case Type<MetadataViews.Traits>():
155 return []
156
157 }
158 return nil
159 }
160
161 }
162
163 // Account's public collection
164 access(all) resource interface CertifilyCollectionPublic {
165
166 // Function to check if an address has view permission for an NFT
167 access(all) view fun hasViewPermission(id: UInt64, address: String): Bool?
168
169 access(all) fun borrowCertifily(id: UInt64): &{NonFungibleToken.NFT}? {
170 // If the result isn't nil, the id of the returned reference
171 // should be the same as the argument to the function
172 post {
173 (result == nil) || (result?.id == id):
174 "Cannot borrow CaaPass reference: The ID of the returned reference is incorrect"
175 }
176 }
177
178 }
179
180 // NFT Collection resource
181 access(all) resource Collection : CertifilyCollectionPublic, NonFungibleToken.Collection {
182
183 /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
184 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
185 let supportedTypes: {Type: Bool} = {}
186 supportedTypes[Type<@Certifily.NFT>()] = true
187 return supportedTypes
188 }
189
190 /// Returns whether or not the given type is accepted by the collection
191 /// A collection that can accept any type should just return true by default
192 access(all) view fun isSupportedNFTType(type: Type): Bool {
193 return type == Type<@Certifily.NFT>()
194 }
195
196
197 /// createEmptyCollection creates an empty Collection for the specified NFT type
198 /// and returns it to the caller so that they can own NFTs
199 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
200 return <- create Collection()
201 }
202
203
204 // Contains caller's list of NFTs
205 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
206
207 init() {
208 self.ownedNFTs <- {}
209 }
210
211 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
212
213 let token <- token as! @Certifily.NFT
214
215 let id: UInt64 = token.id
216
217 // add the new token to the dictionary which removes the old one
218 let oldToken <- self.ownedNFTs[id] <- token
219
220 emit Deposit(id: id, to: self.owner?.address)
221
222 destroy oldToken
223 }
224
225 // function returns token keys of owner
226 access(all) view fun getIDs():[UInt64] {
227 return self.ownedNFTs.keys
228 }
229
230 // function returns token data of token id
231 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
232 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
233 }
234 // Gets a reference to an NFT in the collection as a Certifily,
235 // exposing all of its fields.
236 access(all) view fun borrowCertifily(id: UInt64): &{NonFungibleToken.NFT}? {
237 if self.ownedNFTs[id] != nil {
238 // let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
239 // return ref as! &Certifily.NFT
240 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
241 } else {
242 return nil
243 }
244 }
245
246 // function to check wether the owner have token or not
247 access(all) fun tokenExists(id:UInt64) : Bool {
248 return self.ownedNFTs[id] != nil
249 }
250
251 /// withdraw removes an NFT from the collection and moves it to the caller
252 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID:UInt64) : @{NonFungibleToken.NFT} {
253
254 // let ref = (&self.ownedNFTs[withdrawID] as auth &NonFungibleToken.NFT?)!
255 let ref = (&self.ownedNFTs[withdrawID] as &{NonFungibleToken.NFT}?)
256
257 let val = ref as! &Certifily.NFT
258
259 if val.certType == 1 {
260 panic("Cert Type is not allowed to withdraw")
261 }
262
263 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
264
265 emit Withdraw(id: token.id, from: self.owner?.address)
266
267 return <-token
268
269 }
270
271 // access(all) fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
272 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
273 // let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
274 // let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
275 // let CertifilyNFT = nft as! &Certifily.NFT
276 // return CertifilyNFT as &{ViewResolver.Resolver}
277 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
278 return nft as &{ViewResolver.Resolver}
279 }
280 return nil
281 }
282
283 // Function to add an address to viewPermission
284 access(all) fun addViewPermission(id: UInt64, address: [String]) {
285 // let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
286 let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
287
288 let nft = ref as! &Certifily.NFT?
289
290 // Call the NFT's addViewPermission function
291 nft?.addViewPermission(address: address)
292
293 }
294
295 access(all) fun removeViewPermission(id: UInt64, address: String) {
296 // let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
297 let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
298
299 let nft = ref as! &Certifily.NFT?
300
301 // Call the NFT's removeViewPermission function
302 nft?.removeViewPermission(address: address)
303
304 }
305
306 access(all) view fun hasViewPermission(id: UInt64, address: String): Bool? {
307 // let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
308 let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
309
310 let nft = ref as! &Certifily.NFT?
311
312 // Check if the address has view permission
313 return nft?.viewPermission?.contains(address)
314 }
315
316 }
317
318 /// createEmptyCollection creates an empty Collection for the specified NFT type
319 /// and returns it to the caller so that they can own NFTs
320 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
321 return <- create Collection()
322 }
323
324 /// Function that returns all the Metadata Views implemented by a Non Fungible Token
325 ///
326 /// @return An array of Types defining the implemented views. This value will be used by
327 /// developers to know which parameter to pass to the resolveView() method.
328 ///
329 access(all) view fun getContractViews(resourceType: Type?): [Type] {
330 return [
331 Type<MetadataViews.NFTCollectionData>(),
332 Type<MetadataViews.NFTCollectionDisplay>(),
333 Type<MetadataViews.EVMBridgedMetadata>()
334 ]
335 }
336
337 /// Function that resolves a metadata view for this contract.
338 ///
339 /// @param view: The Type of the desired view.
340 /// @return A structure representing the requested view.
341 ///
342 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
343 switch viewType {
344 case Type<MetadataViews.NFTCollectionData>():
345 let collectionData = MetadataViews.NFTCollectionData(
346 storagePath: Certifily.certifilyStoragePath,
347 publicPath: Certifily.certifilyPublicPath,
348 publicCollection: Type<&Certifily.Collection>(),
349 publicLinkedType: Type<&Certifily.Collection>(),
350 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
351 return <-Certifily.createEmptyCollection(nftType: Type<@Certifily.NFT>())
352 })
353 )
354 return collectionData
355 case Type<MetadataViews.NFTCollectionDisplay>():
356 let media = MetadataViews.Media(
357 file: MetadataViews.HTTPFile(
358 url: "https://certifi.ly/logo512.png"
359 ),
360 mediaType: "image/png+xml"
361 )
362 return MetadataViews.NFTCollectionDisplay(
363 name: "The Example Collection",
364 description: "This collection is used as an example to help you develop your next Flow NFT.",
365 externalURL: MetadataViews.ExternalURL("https://certifi.ly"),
366 squareImage: media,
367 bannerImage: media,
368 socials: {
369 "twitter": MetadataViews.ExternalURL("https://twitter.com/certifily")
370 }
371 )
372 case Type<MetadataViews.EVMBridgedMetadata>():
373 // Implementing this view gives the project control over how the bridged NFT is represented as an ERC721
374 // when bridged to EVM on Flow via the public infrastructure bridge.
375
376 // Compose the contract-level URI. In this case, the contract metadata is located on some HTTP host,
377 // but it could be IPFS, S3, a data URL containing the JSON directly, etc.
378 return MetadataViews.EVMBridgedMetadata(
379 name: "Certifily",
380 symbol: "XMPL",
381 uri: MetadataViews.URI(
382 baseURI: nil, // setting baseURI as nil sets the given value as the uri field value
383 value: "https://certifi.ly"
384 )
385 )
386 }
387 return nil
388 }
389
390 // NFT MINTER
391 access(all) resource NFTMinter {
392 access(all) fun Mint(recipient: Address,content:String, insitution:String, description:String,previewContent:String,certType:UInt8,viewPermission: [String]) : @Certifily.NFT {
393 let token <- create NFT(id: Certifily.totalSupply, content:content, insitution:insitution, description:description, holder: recipient,previewContent:previewContent, certType:certType, viewPermission:viewPermission)
394 emit Mint(id:Certifily.totalSupply,content:content,owner: recipient, insitution:insitution)
395 Certifily.totalSupply = Certifily.totalSupply + 1 as UInt64
396 return <-token
397 }
398 }
399
400 // Contract init
401 init() {
402
403 // total supply is zero at the time of contract deployment
404 self.totalSupply = 0
405
406 self.certifilyStoragePath = /storage/CertifilyNFTCollection
407
408 self.certifilyPublicPath = /public/CertifilyNFTPublicCollection
409
410 self.certifilyMinterPath = /storage/CertifilyNFTMinter
411
412
413 // Create a Collection resource and save it to storage
414 let collection <- create Collection()
415 self.account.storage.save(<-collection, to: self.certifilyStoragePath)
416
417 // create a public capability for the collection
418 let collectionCap = self.account.capabilities.storage.issue<&Certifily.Collection>(self.certifilyStoragePath)
419 self.account.capabilities.publish(collectionCap, at: self.certifilyPublicPath)
420
421 // store a minter resource in account storage
422 self.account.storage.save(<-create NFTMinter(), to: self.certifilyMinterPath)
423
424 emit ContractInitialized()
425
426 }
427
428}
429