Smart Contract
MetaPandaAirdropNFT
A.f2af175e411dfff8.MetaPandaAirdropNFT
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