Smart Contract

YahooPartnersCollectible

A.758252ab932a3416.YahooPartnersCollectible

Deployed

1d ago
Feb 26, 2026, 09:44:37 PM UTC

Dependents

0 imports
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}