Smart Contract
YahooPartnersCollectible
A.758252ab932a3416.YahooPartnersCollectible
1import FungibleToken from 0xf233dcee88fe0abe
2import ViewResolver from 0x1d7e57aa55817448
3import NonFungibleToken from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5
6access(all)
7contract YahooPartnersCollectible: NonFungibleToken {
8 access(all) entitlement AdminEntitlement
9
10 // Events
11 //
12 access(all)
13 event ContractInitialized()
14
15 access(all)
16 event Withdraw(id: UInt64, from: Address?)
17
18 access(all)
19 event Deposit(id: UInt64, to: Address?)
20
21 access(all)
22 event Minted(id: UInt64)
23
24 // Named Paths
25 //
26 access(all)
27 let CollectionStoragePath: StoragePath
28
29 access(all)
30 let CollectionPublicPath: PublicPath
31
32 access(all)
33 let AdminStoragePath: StoragePath
34
35 // totalSupply
36 // The total number of YahooPartnersCollectible that have been minted
37 //
38 access(all)
39 var totalSupply: UInt64
40
41 // metadata for each item
42 //
43 access(contract)
44 var itemMetadata: {UInt64: Metadata}
45
46 // Type Definitions
47 //
48 access(all)
49 struct Metadata {
50 access(all)
51 let name: String
52
53 access(all)
54 let description: String
55
56 // mediaType: MIME type of the media
57 // - image/png
58 // - image/jpeg
59 // - video/mp4
60 // - audio/mpeg
61 access(all)
62 let mediaType: String
63
64 // mediaHash: IPFS storage hash
65 access(all)
66 let mediaHash: String
67
68 // additional metadata
69 access(self)
70 let additional: {String: String}
71
72 // number of items
73 access(all)
74 var itemCount: UInt64
75
76 init(name: String, description: String, mediaType: String, mediaHash: String, additional: {String: String}) {
77 self.name = name
78 self.description = description
79 self.mediaType = mediaType
80 self.mediaHash = mediaHash
81 self.additional = additional
82 self.itemCount = 0
83 }
84
85 access(all)
86 fun getAdditional(): {String: String} {
87 return self.additional
88 }
89
90 access(contract)
91 fun setItemCount(_ itemCount: UInt64) {
92 self.itemCount = itemCount
93 }
94 }
95
96 // NFT
97 // A Yahoo Collectible NFT
98 //
99 access(all)
100 resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
101 // The token's ID
102 access(all)
103 let id: UInt64
104
105 // The token's type
106 access(all)
107 let itemID: UInt64
108
109 // The token's edition number
110 access(all)
111 let editionNumber: UInt64
112
113 // initializer
114 //
115 init(initID: UInt64, itemID: UInt64) {
116 self.id = initID
117 self.itemID = itemID
118 let metadata = YahooPartnersCollectible.itemMetadata[itemID] ?? panic("itemID not valid")
119 self.editionNumber = metadata.itemCount + 1
120
121 // Increment the edition count by 1
122 metadata.setItemCount(self.editionNumber)
123 YahooPartnersCollectible.itemMetadata[itemID] = metadata
124 }
125
126 // Expose metadata
127 access(all)
128 fun getMetadata(): Metadata? {
129 return YahooPartnersCollectible.itemMetadata[self.itemID]
130 }
131
132 access(all)
133 fun getRoyalties(): MetadataViews.Royalties {
134 var royalties: [MetadataViews.Royalty] = []
135 let receiver = getAccount(0x77e38c96fda5c5c5).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
136 royalties.append(MetadataViews.Royalty(receiver: receiver, cut: 0.025, description: "Royalty receiver for Yahoo partners"))
137 return MetadataViews.Royalties(royalties)
138 }
139
140 access(all)
141 fun getEditions(): MetadataViews.Editions {
142 let metadata = self.getMetadata() ?? panic("missing metadata")
143 let editionInfo = MetadataViews.Edition(name: metadata.name, number: self.editionNumber, max: metadata.itemCount)
144 let editionList: [MetadataViews.Edition] = [editionInfo]
145 return MetadataViews.Editions(editionList)
146 }
147
148 access(all)
149 fun getExternalURL(): MetadataViews.ExternalURL {
150 return MetadataViews.ExternalURL("https://bay.blocto.app/flow/yahooPartners/".concat(self.id.toString()))
151 }
152
153 access(all)
154 fun getCollectionData(): MetadataViews.NFTCollectionData {
155 return MetadataViews.NFTCollectionData(
156 storagePath: YahooPartnersCollectible.CollectionStoragePath,
157 publicPath: YahooPartnersCollectible.CollectionPublicPath,
158 publicCollection: Type<&YahooPartnersCollectible.Collection>(),
159 publicLinkedType: Type<&YahooPartnersCollectible.Collection>(),
160 createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection} {
161 return <-YahooPartnersCollectible.createEmptyCollection(nftType: Type<@YahooPartnersCollectible.Collection>())
162 })
163 }
164
165 access(all)
166 fun getCollectionDisplay(): MetadataViews.NFTCollectionDisplay {
167 let squareImage = MetadataViews.Media(
168 file: MetadataViews.HTTPFile(
169 url: "https://raw.githubusercontent.com/portto/assets/main/nft/flow/yahoo/logo.png"),
170 mediaType: "image/png"
171 )
172 let bannerImager = MetadataViews.Media(
173 file: MetadataViews.HTTPFile(
174 url: "https://raw.githubusercontent.com/portto/assets/main/nft/flow/yahoo/banner.png"),
175 mediaType: "image/png"
176 )
177 return MetadataViews.NFTCollectionDisplay(
178 name: "Yahoo Partners",
179 description: "NFT partners of Yahoo Taiwan",
180 externalURL: MetadataViews.ExternalURL("https://bay.blocto.app/market?collections=yahoo_partners"),
181 squareImage: squareImage,
182 bannerImage: bannerImager,
183 socials: {
184 "twitter": MetadataViews.ExternalURL("https://twitter.com/Yahoo")
185 }
186 )
187 }
188
189 access(all)
190 fun getTraits(): MetadataViews.Traits {
191 let metadata = self.getMetadata() ?? panic("missing metadata")
192 return MetadataViews.dictToTraits(dict: metadata.getAdditional(), excludedNames: [])
193 }
194
195 access(all)
196 fun getMedias(): MetadataViews.Medias {
197 let metadata = self.getMetadata() ?? panic("missing metadata")
198 let file = MetadataViews.IPFSFile(cid: metadata.mediaHash, path: nil)
199 let mediaInfo = MetadataViews.Media(file: file, mediaType: metadata.mediaType)
200 let mediaList: [MetadataViews.Media] = [mediaInfo]
201 return MetadataViews.Medias(mediaList)
202 }
203
204 access(all)
205 view fun getViews(): [Type] {
206 return [
207 Type<MetadataViews.Display>(),
208 Type<MetadataViews.Royalties>(),
209 Type<MetadataViews.Editions>(),
210 Type<MetadataViews.ExternalURL>(),
211 Type<MetadataViews.NFTCollectionData>(),
212 Type<MetadataViews.NFTCollectionDisplay>(),
213 Type<MetadataViews.IPFSFile>(),
214 Type<MetadataViews.Traits>(),
215 Type<MetadataViews.Medias>()
216 ]
217 }
218
219 access(all)
220 fun resolveView(_ view: Type): AnyStruct? {
221 switch view {
222 case Type<MetadataViews.Display>():
223 let metadata = self.getMetadata() ?? panic("missing metadata")
224
225 return MetadataViews.Display(
226 name: metadata.name,
227 description: metadata.description,
228 thumbnail: MetadataViews.IPFSFile(
229 cid: metadata.mediaHash,
230 path: nil
231 )
232 )
233
234 case Type<MetadataViews.Royalties>():
235 return self.getRoyalties()
236
237 case Type<MetadataViews.Editions>():
238 return self.getEditions()
239
240 case Type<MetadataViews.ExternalURL>():
241 return self.getExternalURL()
242
243 case Type<MetadataViews.NFTCollectionData>():
244 return self.getCollectionData()
245
246 case Type<MetadataViews.NFTCollectionDisplay>():
247 return self.getCollectionDisplay()
248
249 case Type<MetadataViews.IPFSFile>():
250 return MetadataViews.IPFSFile(
251 cid: self.getMetadata()!.mediaHash,
252 path: nil
253 )
254
255 case Type<MetadataViews.Traits>():
256 return self.getTraits()
257
258 case Type<MetadataViews.Medias>():
259 return self.getMedias()
260 }
261 return nil
262 }
263
264 access(all)
265 fun createEmptyCollection(): @{NonFungibleToken.Collection} {
266 return <-create Collection()
267 }
268 }
269
270 access(all)
271 resource interface CollectionPublic {
272 access(all)
273 fun deposit(token: @{NonFungibleToken.NFT}): Void
274
275 access(all)
276 view fun getIDs(): [UInt64]
277
278 access(all)
279 view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
280
281 access(all)
282 fun borrowYahooPartnersCollectible(id: UInt64): &YahooPartnersCollectible.NFT? {
283 // If the result isn't nil, the id of the returned reference
284 // should be the same as the argument to the function
285 post{
286 result == nil || result?.id == id:
287 "Cannot borrow YahooPartnersCollectible reference: The ID of the returned reference is incorrect"
288 }
289 }
290 }
291
292 // Collection
293 // A collection of YahooPartnersCollectible NFTs owned by an account
294 //
295 access(all)
296 resource Collection: NonFungibleToken.Collection, CollectionPublic {
297 // dictionary of NFT conforming tokens
298 // NFT is a resource type with an `UInt64` ID field
299 //
300 access(all)
301 var ownedNFTs: @{UInt64: { NonFungibleToken.NFT }}
302
303 // withdraw
304 // Removes an NFT from the collection and moves it to the caller
305 //
306 access(NonFungibleToken.Withdraw)
307 fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
308 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
309 emit Withdraw(id: token.id, from: self.owner?.address)
310 return <-token
311 }
312
313 // deposit
314 // Takes a NFT and adds it to the collections dictionary
315 // and adds the ID to the id array
316 //
317 access(all)
318 fun deposit(token: @{NonFungibleToken.NFT}): Void {
319 let token <- token as! @YahooPartnersCollectible.NFT
320 let id: UInt64 = token.id
321
322 // add the new token to the dictionary which removes the old one
323 let oldToken <- self.ownedNFTs[id] <- token
324 emit Deposit(id: id, to: self.owner?.address)
325 destroy oldToken
326 }
327
328 // getIDs
329 // Returns an array of the IDs that are in the collection
330 //
331 access(all)
332 view fun getIDs(): [UInt64] {
333 return self.ownedNFTs.keys
334 }
335
336 // borrowNFT
337 // Gets a reference to an NFT in the collection
338 // so that the caller can read its metadata and call its methods
339 //
340 access(all)
341 view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
342 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
343 }
344
345 // borrowYahooPartnersCollectible
346 // Gets a reference to an NFT in the collection as a YahooPartnersCollectible,
347 // exposing all of its fields.
348 // This is safe as there are no functions that can be called on the YahooPartnersCollectible.
349 //
350 access(all)
351 fun borrowYahooPartnersCollectible(id: UInt64): &YahooPartnersCollectible.NFT? {
352 if self.ownedNFTs[id] != nil{
353 let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
354 return ref as! &YahooPartnersCollectible.NFT
355 } else{
356 return nil
357 }
358 }
359
360 // borrowViewResolver
361 // Gets a reference to an MetadataView.Resolver in the collection as a YahooCollectible.
362 // This is safe as there are no functions that can be called on the YahooCollectible.
363 //
364 access(all)
365 view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
366 let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
367 let exampleNFT = nft as! &YahooPartnersCollectible.NFT
368 return exampleNFT as &{ViewResolver.Resolver}
369 }
370
371 access(all)
372 view fun getSupportedNFTTypes(): {Type: Bool} {
373 let supportedTypes: {Type: Bool} = {}
374 supportedTypes[Type<@NFT>()] = true
375 return supportedTypes
376 }
377
378 access(all)
379 view fun isSupportedNFTType(type: Type): Bool {
380 return type == Type<@NFT>()
381 }
382
383 access(all)
384 fun createEmptyCollection(): @{NonFungibleToken.Collection} {
385 return <-create Collection()
386 }
387
388 // destructor
389 // initializer
390 //
391 init() {
392 self.ownedNFTs <- {}
393 }
394 }
395
396 // createEmptyCollection
397 // public function that anyone can call to create a new empty collection
398 //
399 access(all)
400 fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
401 return <-create Collection()
402 }
403
404 // Admin
405 // Resource that an admin or something similar would own to be
406 // able to mint new NFTs
407 //
408 access(all)
409 resource Admin {
410
411 // mintNFT
412 // Mints a new NFT with a new ID
413 // and deposit it in the recipients collection using their collection reference
414 //
415 access(AdminEntitlement)
416 fun mintNFT(recipient: &{NonFungibleToken.CollectionPublic}, itemID: UInt64, codeHash: String?) {
417 pre{
418 codeHash == nil || !YahooPartnersCollectible.checkCodeHashUsed(codeHash: codeHash!):
419 "duplicated codeHash"
420 }
421 emit Minted(id: YahooPartnersCollectible.totalSupply)
422
423 // deposit it in the recipient's account using their reference
424 recipient.deposit(token: <-create YahooPartnersCollectible.NFT(initID: YahooPartnersCollectible.totalSupply, itemID: itemID))
425 YahooPartnersCollectible.totalSupply = YahooPartnersCollectible.totalSupply + 1
426
427 // if minter passed in codeHash, register it to dictionary
428 if let checkedCodeHash = codeHash{
429 let redeemedCodes = YahooPartnersCollectible.account.storage.load<{String: Bool}>(from: /storage/redeemedCodes)!
430 redeemedCodes[checkedCodeHash] = true
431 YahooPartnersCollectible.account.storage.save<{String: Bool}>(redeemedCodes, to: /storage/redeemedCodes)
432 }
433 }
434
435 // batchMintNFT
436 // Mints a batch of new NFTs
437 // and deposit it in the recipients collection using their collection reference
438 //
439 access(AdminEntitlement)
440 fun batchMintNFT(recipient: &{NonFungibleToken.CollectionPublic}, itemID: UInt64, count: Int) {
441 var index = 0
442
443 while index < count {
444 self.mintNFT(
445 recipient: recipient,
446 itemID: itemID,
447 codeHash: nil
448 )
449
450 index = index + 1
451 }
452 }
453
454 // registerMetadata
455 // Registers metadata for a itemID
456 //
457 access(AdminEntitlement)
458 fun registerMetadata(itemID: UInt64, metadata: Metadata) {
459 pre{
460 YahooPartnersCollectible.itemMetadata[itemID] == nil:
461 "duplicated itemID"
462 }
463 YahooPartnersCollectible.itemMetadata[itemID] = metadata
464 }
465
466 // updateMetadata
467 // Registers metadata for a itemID
468 //
469 access(AdminEntitlement)
470 fun updateMetadata(itemID: UInt64, metadata: Metadata) {
471 pre{
472 YahooPartnersCollectible.itemMetadata[itemID] != nil:
473 "itemID does not exist"
474 }
475 metadata.setItemCount(YahooPartnersCollectible.itemMetadata[itemID]!.itemCount)
476
477 // update metadata
478 YahooPartnersCollectible.itemMetadata[itemID] = metadata
479 }
480 }
481
482 // fetch
483 // Get a reference to a YahooPartnersCollectible from an account's Collection, if available.
484 // If an account does not have a YahooPartnersCollectible.Collection, panic.
485 // If it has a collection but does not contain the itemID, return nil.
486 // If it has a collection and that collection contains the itemID, return a reference to that.
487 //
488 access(all)
489 fun fetch(_ from: Address, itemID: UInt64): &YahooPartnersCollectible.NFT? {
490 let collection = getAccount(from)
491 .capabilities
492 .borrow<&YahooPartnersCollectible.Collection>(YahooPartnersCollectible.CollectionPublicPath)
493 ?? panic("Couldn't get collection")
494 // We trust YahooPartnersCollectible.Collection.borrowYahooPartnersCollectible to get the correct itemID
495 // (it checks it before returning it).
496 return collection.borrowYahooPartnersCollectible(id: itemID)
497 }
498
499 // getMetadata
500 // Get the metadata for a specific type of YahooPartnersCollectible
501 //
502 access(all)
503 fun getMetadata(itemID: UInt64): Metadata? {
504 return YahooPartnersCollectible.itemMetadata[itemID]
505 }
506
507 // checkCodeHashUsed
508 // Check if a codeHash has been registered
509 //
510 access(all)
511 view fun checkCodeHashUsed(codeHash: String): Bool {
512 var redeemedCodes = YahooPartnersCollectible.account.storage.copy<{String: Bool}>(from: /storage/redeemedCodes)!
513 return redeemedCodes[codeHash] ?? false
514 }
515
516 access(all)
517 view fun getContractViews(resourceType: Type?): [Type] {
518 return [
519 Type<MetadataViews.NFTCollectionData>(),
520 Type<MetadataViews.NFTCollectionDisplay>()
521 ]
522 }
523
524 access(all)
525 view fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
526 return [
527 Type<MetadataViews.NFTCollectionData>(),
528 Type<MetadataViews.NFTCollectionDisplay>()
529 ]
530 }
531
532 // initializer
533 //
534 init() {
535 // Set our named paths
536 self.CollectionStoragePath = /storage/yahooPartnersCollectibleCollection
537 self.CollectionPublicPath = /public/yahooPartnersCollectibleCollection
538 self.AdminStoragePath = /storage/yahooPartnersCollectibleAdmin
539
540 // Initialize the total supply
541 self.totalSupply = 0
542
543 // Initialize predefined metadata
544 self.itemMetadata = {}
545
546 // Create a Admin resource and save it to storage
547 let minter <- create Admin()
548 self.account.storage.save(<-minter, to: self.AdminStoragePath)
549
550 emit ContractInitialized()
551 }
552}