Smart Contract

MetaPandaAirdropNFT

A.f2af175e411dfff8.MetaPandaAirdropNFT

Deployed

2h ago
Feb 28, 2026, 11:20:45 PM UTC

Dependents

0 imports
1/**
2  This program is free software: you can redistribute it and/or modify
3  it under the terms of the GNU General Public License as published by
4  the Free Software Foundation, either version 3 of the License, or
5  (at your option) any later version.
6
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  GNU General Public License for more details.
11
12  You should have received a copy of the GNU General Public License
13  along with this program.  If not, see <https://www.gnu.org/licenses/>.
14**/
15import NonFungibleToken from 0x1d7e57aa55817448
16import MetadataViews from 0x1d7e57aa55817448
17import AnchainUtils from 0x7ba45bdcac17806a
18
19pub contract MetaPandaAirdropNFT: NonFungibleToken {
20  access(contract) var collectionBannerURL: String
21  access(contract) var websiteURL: String
22  access(contract) var nftURL: String
23  pub var totalSupply: UInt64
24
25  pub event ContractInitialized()
26  pub event Withdraw(id: UInt64, from: Address?)
27  pub event Deposit(id: UInt64, to: Address?)
28  pub event Minted(id: UInt64, nftType: String, metadata: {String:String})
29  pub event Transfer(id: UInt64, from: Address?, to: Address?)
30  pub event Burned(id: UInt64, address: Address?)
31
32  pub let CollectionStoragePath: StoragePath
33  pub let CollectionPublicPath: PublicPath
34  pub let MinterStoragePath: StoragePath
35
36  pub struct MetaPandaAirdropView {
37    pub let uuid: UInt64
38    pub let id: UInt64
39    pub let nftType: String
40    pub let file: AnyStruct{MetadataViews.File}
41    access(self) let metadata: {String:String}
42
43    init(
44      uuid: UInt64,
45      id: UInt64,
46      nftType: String,
47      metadata: {String:String},
48      file: AnyStruct{MetadataViews.File}
49    ) {
50      self.uuid = uuid
51      self.id = id
52      self.nftType = nftType
53      self.metadata = metadata
54      self.file = file
55    }
56
57    pub fun getMetadata(): {String:String} {
58      return self.metadata
59    }
60  }
61
62  pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
63    pub let id: UInt64
64    pub let nftType: String
65    pub let file: AnyStruct{MetadataViews.File}
66    access(self) let metadata: {String:String}
67    access(self) let royalties: [MetadataViews.Royalty]
68
69    init(
70      nftType: String,
71      metadata: {String:String},
72      file: AnyStruct{MetadataViews.File},
73      royalties: [MetadataViews.Royalty]
74    ) {
75      self.id = MetaPandaAirdropNFT.totalSupply
76      self.nftType = nftType
77      self.metadata = metadata
78      self.file = file
79      self.royalties = royalties
80
81      emit Minted(
82        id: self.id,
83        nftType: self.nftType,
84        metadata: self.metadata
85      )
86
87      MetaPandaAirdropNFT.totalSupply = MetaPandaAirdropNFT.totalSupply + 1
88    }
89  
90    pub fun getViews(): [Type] {
91      return [
92        Type<MetaPandaAirdropView>(),
93        Type<MetadataViews.Display>(),
94        Type<MetadataViews.Royalties>(),
95        Type<MetadataViews.Editions>(),
96        Type<MetadataViews.ExternalURL>(),
97        Type<MetadataViews.NFTCollectionData>(),
98        Type<MetadataViews.NFTCollectionDisplay>(),
99        Type<MetadataViews.Serial>(),
100        Type<MetadataViews.Traits>()
101      ]
102    }
103
104    pub fun resolveView(_ view: Type): AnyStruct? {
105      switch view {
106        case Type<MetaPandaAirdropView>():
107          return MetaPandaAirdropView(
108            uuid: self.uuid,
109            id: self.id,
110            nftType: self.nftType,
111            metadata: self.metadata,
112            file: self.file
113          )
114
115        case Type<MetadataViews.Display>():
116          let file = self.file as! MetadataViews.IPFSFile
117          return MetadataViews.Display(
118            name: self.metadata["name"] ?? "Meta Panda Collectible NFT",
119            description: self.metadata["description"] ?? "Meta Panda Collectible NFT #".concat(self.id.toString()),
120            thumbnail: MetadataViews.HTTPFile(
121              url: "https://ipfs.tenzingai.com/ipfs/".concat(file.cid)
122            )
123          )
124
125        case Type<MetadataViews.Editions>():
126          // There is no max number of NFTs that can be minted from this contract
127          // so the max edition field value is set to nil
128          return MetadataViews.Editions([
129            MetadataViews.Edition(
130              name: "Meta Panda Airdrop NFT Edition", 
131              number: self.id, 
132              max: nil
133            )
134          ])
135
136        case Type<MetadataViews.Serial>():
137          return MetadataViews.Serial(self.id)
138
139        case Type<MetadataViews.Royalties>():
140          return MetadataViews.Royalties(self.royalties)
141
142        case Type<MetadataViews.ExternalURL>():
143          return MetadataViews.ExternalURL(MetaPandaAirdropNFT.nftURL.concat("/").concat(self.id.toString()))
144
145        case Type<MetadataViews.NFTCollectionData>():
146          return MetadataViews.NFTCollectionData(
147            storagePath: MetaPandaAirdropNFT.CollectionStoragePath,
148            publicPath: MetaPandaAirdropNFT.CollectionPublicPath,
149            providerPath: /private/MetaPandaAirdropNFTCollection,
150            publicCollection: Type<&MetaPandaAirdropNFT.Collection{AnchainUtils.ResolverCollection,NonFungibleToken.CollectionPublic,MetadataViews.ResolverCollection}>(),
151            publicLinkedType: Type<&MetaPandaAirdropNFT.Collection{AnchainUtils.ResolverCollection,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(),
152            providerLinkedType: Type<&MetaPandaAirdropNFT.Collection{AnchainUtils.ResolverCollection,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(),
153            createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
154              return <-MetaPandaAirdropNFT.createEmptyCollection()
155            })
156          )
157
158        case Type<MetadataViews.NFTCollectionDisplay>():
159          let media = MetadataViews.Media(
160            file: MetadataViews.HTTPFile(url: MetaPandaAirdropNFT.collectionBannerURL),
161            mediaType: "image/png"
162          )
163          return MetadataViews.NFTCollectionDisplay(
164            name: "The Meta Panda Airdrop NFT Collection",
165            description: "",
166            externalURL: MetadataViews.ExternalURL(MetaPandaAirdropNFT.websiteURL),
167            squareImage: media,
168            bannerImage: media,
169            socials: {}
170          )
171
172        case Type<MetadataViews.Traits>():
173          return MetadataViews.dictToTraits(dict: self.metadata, excludedNames: [])
174      }
175      return nil
176    }
177
178    destroy() {
179      emit Burned(id: self.id, address: self.owner?.address)
180    }
181  }
182
183  pub resource Collection: AnchainUtils.ResolverCollection, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
184    // dictionary of NFT conforming tokens
185    // NFT is a resource type with an `UInt64` ID field
186    pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
187
188    init () {
189      self.ownedNFTs <- {}
190    }
191
192    // withdraw removes an NFT from the collection and moves it to the caller
193    pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
194      let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
195
196      emit Withdraw(id: token.id, from: self.owner?.address)
197
198      return <-token
199    }
200
201    // deposit takes a NFT and adds it to the collections dictionary
202    // and adds the ID to the id array
203    pub fun deposit(token: @NonFungibleToken.NFT) {
204      let token <- token as! @MetaPandaAirdropNFT.NFT
205
206      let id: UInt64 = token.id
207
208      // add the new token to the dictionary which removes the old one
209      let oldToken <- self.ownedNFTs[id] <- token
210
211      emit Deposit(id: id, to: self.owner?.address)
212
213      destroy oldToken
214    }
215
216    // transfer takes an NFT ID and a reference to a recipient's collection
217    // and transfers the NFT corresponding to that ID to the recipient
218    pub fun transfer(id: UInt64, recipient: &{NonFungibleToken.CollectionPublic}) {
219      post {
220        self.ownedNFTs[id] == nil: "The specified NFT was not transferred"
221        recipient.borrowNFT(id: id) != nil: "Recipient did not receive the intended NFT"
222      }
223
224      let nft <- self.withdraw(withdrawID: id)
225      
226      recipient.deposit(token: <- nft)
227      
228      emit Transfer(id: id, from: self.owner?.address, to: recipient.owner?.address)
229    }
230
231    // burn destroys an NFT
232    pub fun burn(id: UInt64) {
233      post {
234        self.ownedNFTs[id] == nil: "The specified NFT was not burned"
235      }
236
237      // This will emit a burn event
238      destroy <- self.withdraw(withdrawID: id)
239    }
240
241    // getIDs returns an array of the IDs that are in the collection
242    pub fun getIDs(): [UInt64] {
243      return self.ownedNFTs.keys
244    }
245
246    // borrowNFT gets a reference to an NFT in the collection
247    // so that the caller can read its metadata and call its methods
248    pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
249      if let nft = &self.ownedNFTs[id] as &NonFungibleToken.NFT? {
250        return nft
251      }
252      panic("NFT not found in collection.")
253    }
254
255    pub fun borrowViewResolverSafe(id: UInt64): &AnyResource{MetadataViews.Resolver}? {
256      if let nft = &self.ownedNFTs[id] as auth &NonFungibleToken.NFT? {
257        return nft as! &MetaPandaAirdropNFT.NFT
258      }
259      return nil
260    }
261
262    pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
263      if let nft = &self.ownedNFTs[id] as auth &NonFungibleToken.NFT? {
264        return nft as! &MetaPandaAirdropNFT.NFT
265      }
266      panic("NFT not found in collection.")
267    }
268
269    destroy() {
270      destroy self.ownedNFTs
271    }
272  }
273
274  // public function that anyone can call to create a new empty collection
275  pub fun createEmptyCollection(): @NonFungibleToken.Collection {
276    return <- create Collection()
277  }
278
279  // Resource that an admin or something similar would own to be
280  // able to mint new NFTs
281  //
282  pub resource NFTMinter {
283    // mintNFT mints a new NFT with a new ID
284    // and deposit it in the recipients collection using their collection reference
285    pub fun mintNFT(
286      recipient: &{NonFungibleToken.CollectionPublic},
287      nftType: String,
288      metadata: {String:String},
289      file: AnyStruct{MetadataViews.File},
290      royalties: [MetadataViews.Royalty]
291    ) {
292      // create a new NFT
293      let newNFT <- create NFT(
294        nftType: nftType,
295        metadata: metadata,
296        file: file,
297        royalties: royalties
298      )
299
300      // deposit it in the recipient's account using their reference
301      recipient.deposit(token: <-newNFT)
302    }
303
304    pub fun updateURLs(collectionBannerURL: String?, websiteURL: String?, nftURL: String?) {
305      if collectionBannerURL != nil {
306        MetaPandaAirdropNFT.collectionBannerURL = collectionBannerURL!
307      }
308      if websiteURL != nil {
309        MetaPandaAirdropNFT.websiteURL = websiteURL!
310      }
311      if nftURL != nil {
312        MetaPandaAirdropNFT.nftURL = nftURL!
313      }
314    }
315  }
316
317  init() {
318    // Initialize the total supply
319    self.totalSupply = 0
320
321    // Set the named paths
322    self.CollectionStoragePath = /storage/MetaPandaAirdropNFTCollection
323    self.CollectionPublicPath = /public/MetaPandaAirdropNFTCollection
324    self.MinterStoragePath = /storage/MetaPandaAirdropNFTMinter
325
326    // External URLs
327    self.collectionBannerURL = "http://ipfs.io/ipfs/QmTPNGKfBHJUWXaAjHr2S98QbGeB9LmNSrLfLp6AyjNixf"
328    self.websiteURL = "https://metapandaclub.com/"
329    self.nftURL = "https://s3.us-west-2.amazonaws.com/nft.pandas"
330
331    // Create a Collection resource and save it to storage
332    let collection <- create Collection()
333    self.account.save(<-collection, to: self.CollectionStoragePath)
334
335    // create a public capability for the collection
336    self.account.link<&MetaPandaAirdropNFT.Collection{AnchainUtils.ResolverCollection,NonFungibleToken.CollectionPublic,MetadataViews.ResolverCollection}>(
337      self.CollectionPublicPath, 
338      target: self.CollectionStoragePath
339    )
340
341    // Create a Minter resource and save it to storage
342    let minter <- create NFTMinter()
343    self.account.save(<-minter, to: self.MinterStoragePath)
344
345    emit ContractInitialized()
346  }
347}
348