Smart Contract
Pack
A.de7a5daf9df48c65.Pack
1import BasicBeasts from 0xde7a5daf9df48c65
2import ViewResolver from 0x1d7e57aa55817448
3import HunterScore from 0xde7a5daf9df48c65
4import NonFungibleToken from 0x1d7e57aa55817448
5import FungibleToken from 0xf233dcee88fe0abe
6import MetadataViews from 0x1d7e57aa55817448
7
8access(all) contract Pack: NonFungibleToken {
9
10 // -----------------------------------------------------------------------
11 // Entitlements
12 // -----------------------------------------------------------------------
13 access(all) entitlement NFTOwner
14 access(all) entitlement PackManagerOwner
15 access(all) entitlement CollectionOwner
16 access(all) entitlement AdminOwner
17
18 // -----------------------------------------------------------------------
19 // NonFungibleToken Standard Events
20 // -----------------------------------------------------------------------
21 access(all) event ContractInitialized()
22 access(all) event Withdraw(id: UInt64, from: Address?)
23 access(all) event Deposit(id: UInt64, to: Address?)
24
25 // -----------------------------------------------------------------------
26 // Pack Events
27 // -----------------------------------------------------------------------
28 access(all) event PackOpened(id: UInt64, packTemplateID: UInt32, beastID: UInt64, beastTemplateID: UInt32, serialNumber: UInt32, sex: String, firstOwner: Address?)
29 access(all) event PackTemplateCreated(packTemplateID: UInt32, name: String)
30 access(all) event PackMinted(id: UInt64, name: String)
31
32 // -----------------------------------------------------------------------
33 // Named Paths
34 // -----------------------------------------------------------------------
35 access(all) let PackManagerStoragePath: StoragePath
36 access(all) let PackManagerPublicPath: PublicPath
37 access(all) let CollectionStoragePath: StoragePath
38 access(all) let CollectionPublicPath: PublicPath
39 access(all) let AdminStoragePath: StoragePath
40 access(all) let AdminPrivatePath: PrivatePath
41
42 // -----------------------------------------------------------------------
43 // NonFungibleToken Standard Fields
44 // -----------------------------------------------------------------------
45 access(all) var totalSupply: UInt64
46
47 // -----------------------------------------------------------------------
48 // Pack Fields
49 // -----------------------------------------------------------------------
50 access(self) var packTemplates:{ UInt32: PackTemplate}
51 access(self) var stockNumbers: [UInt64]
52 access(self) var numberMintedPerPackTemplate:{ UInt32: UInt32}
53
54 access(all) struct PackTemplate {
55 access(all) let packTemplateID: UInt32
56 access(all) let name: String
57 access(all) let image: String
58 access(all) let description: String
59
60 init(packTemplateID: UInt32, name: String, image: String, description: String){
61 pre{
62 name != "":
63 "Can't create PackTemplate: name can't be blank"
64 image != "":
65 "Can't create PackTemplate: image can't be blank"
66 description != "":
67 "Can't create PackTemplate: description can't be blank"
68 }
69 self.packTemplateID = packTemplateID
70 self.name = name
71 self.image = image
72 self.description = description
73 }
74 }
75
76 access(all) resource interface Public {
77 access(all) let id: UInt64
78 access(all) let stockNumber: UInt64
79 access(all) let serialNumber: UInt32
80 access(all) let packTemplate: PackTemplate
81 access(all) var opened: Bool
82 access(all) view fun isOpened(): Bool
83 access(all) view fun containsFungibleTokens(): Bool
84 access(all) view fun containsBeast(): Bool
85 access(all) view fun getNumberOfFungibleTokenVaults(): Int
86 access(all) view fun getNumberOfBeasts(): Int
87 }
88
89 access(all) resource NFT: NonFungibleToken.NFT, Public, ViewResolver.Resolver {
90 access(all) let id: UInt64
91 access(all) let stockNumber: UInt64
92 access(all) let serialNumber: UInt32
93 access(all) let packTemplate: PackTemplate
94 access(all) var opened: Bool
95 access(contract) var fungibleTokens: @[{FungibleToken.Vault}]
96 access(contract) var beast: @{UInt64: BasicBeasts.NFT}
97
98 init(stockNumber: UInt64, packTemplateID: UInt32){
99 pre{
100 !Pack.stockNumbers.contains(stockNumber):
101 "Can't mint Pack NFT: pack stock number has already been minted"
102 Pack.packTemplates[packTemplateID] != nil:
103 "Can't mint Pack NFT: packTemplate does not exist"
104 }
105 Pack.totalSupply = Pack.totalSupply + 1
106 Pack.stockNumbers.append(stockNumber)
107 Pack.numberMintedPerPackTemplate[packTemplateID] = Pack.numberMintedPerPackTemplate[packTemplateID]! + 1
108 self.serialNumber = Pack.numberMintedPerPackTemplate[packTemplateID]!
109 self.id = stockNumber
110 self.stockNumber = stockNumber
111 self.packTemplate = Pack.packTemplates[packTemplateID]!
112 self.opened = false
113 self.fungibleTokens <- []
114 self.beast <-{}
115 }
116
117 access(NFTOwner) fun retrieveAllFungibleTokens(): @[{FungibleToken.Vault}] {
118 pre {
119 self.containsFungibleTokens():
120 "Can't retrieve fungible token vaults: Pack does not contain vaults"
121 }
122 var tokens: @[{FungibleToken.Vault}] <- []
123 self.fungibleTokens <-> tokens
124 return <-tokens
125 }
126
127 access(contract) fun updateIsOpened() {
128 if self.beast.keys.length == 0 {
129 self.opened = true
130 }
131 }
132
133 access(contract) fun insertBeast(beast: @BasicBeasts.NFT) {
134 pre {
135 self.beast.keys.length == 0:
136 "Can't insert Beast into Pack: Pack already contain a Beast"
137 !self.isOpened():
138 "Can't insert Beast into Pack: Pack has already been opened"
139 }
140 let id = beast.id
141 self.beast[id] <-! beast
142 }
143
144 access(contract) fun retrieveBeast(): @BasicBeasts.NFT? {
145 if self.containsBeast(){
146 let keys = self.beast.keys
147 return <-self.beast.remove(key: keys[0])!
148 }
149 return nil
150 }
151
152 access(contract) fun insertFungible(vault: @{FungibleToken.Vault}) {
153 self.fungibleTokens.append(<-vault)
154 }
155
156 access(all) view fun isOpened(): Bool{
157 return self.opened
158 }
159
160 access(all) view fun containsFungibleTokens(): Bool{
161 return self.fungibleTokens.length > 0
162 }
163
164 access(all) view fun containsBeast(): Bool {
165 return self.beast.keys.length > 0
166 }
167
168 access(all) view fun getNumberOfFungibleTokenVaults(): Int {
169 return self.fungibleTokens.length
170 }
171
172 access(all) view fun getNumberOfBeasts(): Int {
173 return self.beast.keys.length
174 }
175
176 access(all) view fun getViews(): [Type] {
177 return [Type<MetadataViews.Display>()]
178 }
179
180 access(all) view fun resolveView(_ view: Type): AnyStruct? {
181 switch view{
182 case Type<MetadataViews.Display>():
183 return MetadataViews.Display(name: self.packTemplate.name, description: self.packTemplate.description, thumbnail: MetadataViews.IPFSFile(cid: self.packTemplate.image, path: nil))
184 }
185 return nil
186 }
187
188 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection}{
189 return <-create Collection()
190 }
191 }
192
193 access(all) resource interface PublicPackManager {
194 access(all) let id: UInt64
195 }
196
197 // A Pack Manager resource allows the holder to retrieve a beast from a pack and destroy the pack NFT after it has been unpacked.
198 access(all) resource PackManager: PublicPackManager {
199 access(all) let id: UInt64
200
201 init(){
202 self.id = self.uuid
203 }
204
205 access(PackManagerOwner) fun retrieveBeast(pack: @NFT): @BasicBeasts.Collection {
206 pre{
207 pack.containsBeast():
208 "Can't retrieve beast: Pack does not contain a beast"
209 self.owner != nil:
210 "Can't retrieve beast: self.owner is nil"
211 }
212 let keys = pack.beast.keys
213 let beastCollection <- BasicBeasts.createEmptyCollection(nftType: Type<@BasicBeasts.Collection>()) as! @BasicBeasts.Collection
214 let beastRef: &BasicBeasts.NFT = (&pack.beast[keys[0]] as &BasicBeasts.NFT?)!
215 let beast <- pack.retrieveBeast()!
216 beast.setFirstOwner(firstOwner: (self.owner!).address)
217 beastCollection.deposit(token: <-beast)
218 let newBeastCollection <- HunterScore.increaseHunterScore(wallet: (self.owner!).address, beasts: <-beastCollection)
219 pack.updateIsOpened()
220 if pack.isOpened(){
221 emit PackOpened(id: pack.id, packTemplateID: pack.packTemplate.packTemplateID, beastID: beastRef.id, beastTemplateID: beastRef.getBeastTemplate().beastTemplateID, serialNumber: beastRef.serialNumber, sex: beastRef.sex, firstOwner: beastRef.getFirstOwner())
222 }
223 destroy pack
224 return <-newBeastCollection
225 }
226 }
227
228 // -----------------------------------------------------------------------
229 // Admin Resource Functions
230 //
231 // Admin is a special authorization resource that
232 // allows the owner to perform important NFT functions
233 // -----------------------------------------------------------------------
234 access(all) resource Admin {
235 access(AdminOwner) fun createPackTemplate(packTemplateID: UInt32, name: String, image: String, description: String): UInt32 {
236 pre{
237 Pack.packTemplates[packTemplateID] == nil:
238 "Can't create PackTemplate: Pack Template ID already exist"
239 }
240 var newPackTemplate = PackTemplate(packTemplateID: packTemplateID, name: name, image: image, description: description)
241 Pack.packTemplates[packTemplateID] = newPackTemplate
242 Pack.numberMintedPerPackTemplate[packTemplateID] = 0
243 emit PackTemplateCreated(packTemplateID: newPackTemplate.packTemplateID, name: newPackTemplate.name)
244 return newPackTemplate.packTemplateID
245 }
246
247 access(AdminOwner) fun mintPack(stockNumber: UInt64, packTemplateID: UInt32): @Pack.NFT {
248 let newPack: @Pack.NFT <- Pack.mintPack(stockNumber: stockNumber, packTemplateID: packTemplateID)
249 return <-newPack
250 }
251
252 access(AdminOwner) fun insertBeast(pack: @Pack.NFT, beast: @BasicBeasts.NFT): @Pack.NFT {
253 pre{
254 pack.beast.keys.length == 0:
255 "Can't insert Beast into Pack: Pack already contain a Beast"
256 !pack.isOpened():
257 "Can't insert Beast into Pack: Pack has already been opened"
258 }
259 let id = beast.id
260 pack.insertBeast(beast: <-beast)
261 return <-pack
262 }
263
264 access(AdminOwner) fun insertFungible(pack: @Pack.NFT, vault: @{FungibleToken.Vault}): @Pack.NFT {
265 pack.insertFungible(vault: <-vault)
266 return <-pack
267 }
268
269 access(AdminOwner) fun createNewAdmin(): @Admin {
270 return <-create Admin()
271 }
272 }
273
274 access(all) resource interface PackCollectionPublic{
275 access(all) fun deposit(token: @{NonFungibleToken.NFT}): Void
276 access(all) view fun getIDs(): [UInt64]
277 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
278 access(all) view fun borrowPack(id: UInt64): &Pack.NFT? {
279 post{
280 result == nil || result?.id == id:
281 "Cannot borrow Pack reference: The ID of the returned reference is incorrect"
282 }
283 }
284 }
285
286 access(all) resource Collection: PackCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection {
287 access(all) var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
288
289 init(){
290 self.ownedNFTs <-{}
291 }
292
293 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{
294 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Cannot withdraw: The Pack does not exist in the Collection")
295 emit Withdraw(id: token.id, from: self.owner?.address)
296 return <-token
297 }
298
299 access(all) fun deposit(token: @{NonFungibleToken.NFT}): Void{
300 let token <- token as! @Pack.NFT
301 let id = token.id
302 let oldToken <- self.ownedNFTs[id] <- token
303 if self.owner?.address != nil{
304 emit Deposit(id: id, to: self.owner?.address)
305 }
306 destroy oldToken
307 }
308
309 access(all) view fun getIDs(): [UInt64]{
310 return self.ownedNFTs.keys
311 }
312
313 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{
314 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
315 }
316
317 access(all) view fun borrowPack(id: UInt64): &Pack.NFT?{
318 let ref = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}?
319 return ref as! &Pack.NFT?
320 }
321
322 access(CollectionOwner) fun borrowEntirePack(id: UInt64): &Pack.NFT?{
323 let ref = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}?
324 return ref as! &Pack.NFT?
325 }
326
327 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?{
328 let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
329 let packNFT = nft as! &Pack.NFT
330 return packNFT
331 }
332
333 access(all) view fun getSupportedNFTTypes():{ Type: Bool} {
334 return {
335 Type<@Pack.NFT>(): true
336 }
337 }
338
339 access(all) view fun isSupportedNFTType(type: Type): Bool {
340 return type == Type<@Pack.NFT>()
341 }
342
343 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection}{
344 return <-create Collection()
345 }
346 }
347
348 // -----------------------------------------------------------------------
349 // Access(Account) Functions
350 // -----------------------------------------------------------------------
351 access(account) fun mintPack(stockNumber: UInt64, packTemplateID: UInt32): @Pack.NFT {
352 let newPack: @Pack.NFT <- create NFT(stockNumber: stockNumber, packTemplateID: packTemplateID)
353 emit PackMinted(id: newPack.id, name: newPack.packTemplate.name)
354 return <-newPack
355 }
356
357 // -----------------------------------------------------------------------
358 // Public Functions
359 // -----------------------------------------------------------------------
360 access(all) fun createNewPackManager(): @PackManager {
361 return <-create PackManager()
362 }
363
364 access(all) view fun getAllPackTemplates():{ UInt32: PackTemplate} {
365 return self.packTemplates
366 }
367
368 access(all) view fun getPackTemplate(packTemplateID: UInt32): PackTemplate? {
369 return self.packTemplates[packTemplateID]
370 }
371
372 access(all) view fun getAllstockNumbers(): [UInt64] {
373 return self.stockNumbers
374 }
375
376 access(all) view fun isMinted(stockNumber: UInt64): Bool {
377 return self.stockNumbers.contains(stockNumber)
378 }
379
380 access(all) view fun getAllNumberMintedPerPackTemplate():{ UInt32: UInt32} {
381 return self.numberMintedPerPackTemplate
382 }
383
384 access(all) view fun getNumberMintedPerPackTemplate(packTemplateID: UInt32): UInt32? {
385 return self.numberMintedPerPackTemplate[packTemplateID]
386 }
387
388 // -----------------------------------------------------------------------
389 // NonFungibleToken Standard Functions
390 // -----------------------------------------------------------------------
391 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{
392 return <-create self.Collection()
393 }
394
395 // -----------------------------------------------------------------------
396 // MetadataViews - Purposely Empty
397 // -----------------------------------------------------------------------
398 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
399 return nil
400 }
401
402 access(all) view fun getContractViews(resourceType: Type?): [Type] {
403 return []
404 }
405
406 init(){
407 // Set named paths
408 self.PackManagerStoragePath = /storage/BasicBeastsPackManager
409 self.PackManagerPublicPath = /public/BasicBeastsPackManager
410 self.CollectionStoragePath = /storage/BasicBeastsPackCollection
411 self.CollectionPublicPath = /public/BasicBeastsPackCollection
412 self.AdminStoragePath = /storage/BasicBeastsPackAdmin
413 self.AdminPrivatePath = /private/BasicBeastsPackAdminUpgrade
414
415 // Initialize the fields
416 self.totalSupply = 0
417 self.packTemplates ={}
418 self.stockNumbers = []
419 self.numberMintedPerPackTemplate ={}
420
421 // Put Admin in storage
422 self.account.storage.save(<-create Admin(), to: self.AdminStoragePath)
423 emit ContractInitialized()
424 }
425}
426// Thank you swt and raven for reviewing this pack contract with me. Jacob sucks...
427
428