Smart Contract
Bag
A.c649103ebbc38926.Bag
1import NonFungibleToken from 0x1d7e57aa55817448
2import FungibleToken from 0xf233dcee88fe0abe
3import FlowToken from 0x1654653399040a61
4import MetadataViews from 0x1d7e57aa55817448
5import ViewResolver from 0x1d7e57aa55817448
6
7import Base64Util from 0xa45ead1cf1ca9eda
8import BagRegistry from 0xc649103ebbc38926
9
10import Background from 0xc649103ebbc38926
11import Body from 0xc649103ebbc38926
12import Cloth from 0xc649103ebbc38926
13import Weapon from 0xc649103ebbc38926
14import Glove from 0xc649103ebbc38926
15import Ring from 0xc649103ebbc38926
16import Helmet from 0xc649103ebbc38926
17import Rarity from 0xc649103ebbc38926
18
19access(all) contract Bag: NonFungibleToken, ViewResolver {
20
21 /* --- Events --- */
22 access(all) event ContractInitialized()
23 access(all) event NFTWithdrawn(id: UInt64, from: Address?)
24 access(all) event NFTDeposited(id: UInt64, to: Address?)
25 access(all) event NFTMinted(id: UInt64, svg: String, mintedFor: Address)
26 access(all) event PriceUpdated(oldPrice: UFix64, newPrice: UFix64)
27
28 /* --- Storage Paths --- */
29 access(all) let CollectionStoragePath: StoragePath
30 access(all) let CollectionPublicPath: PublicPath
31 access(all) let CollectionPrivatePath: PrivatePath
32 access(all) let AdminStoragePath: StoragePath
33
34 /* --- Contract State --- */
35 access(all) var totalSupply: UInt64
36 access(all) let maxSupply: UInt64
37 access(all) var mintPrice: UFix64
38 access(all) let reservedSupply: UInt64
39 access(all) var reservedMinted: UInt64
40 access(all) var traitsDetails: {UInt64: Bag.TraitsDetails}
41 access(all) var bagRarityScores: {UInt64: UInt64}
42 access(all) var uniqueTraitsCombinations: [String]
43 access(all) let registryAddress: Address
44 access(self) let owner: Address
45
46 // Price update configuration
47 access(all) var priceUpdateInterval: UInt64
48 access(all) let priceIncreaseAmount: UFix64
49
50 access(all) struct TraitsDetails {
51 access(all) var background: String
52 access(all) var body: String
53 access(all) var cloth: String
54 access(all) var glove: String
55 access(all) var helmet: String
56 access(all) var ring: String
57 access(all) var weapon: String
58
59 init(background: String, body: String, cloth: String, glove: String, helmet: String, ring: String, weapon: String) {
60 self.background = background
61 self.body = body
62 self.cloth = cloth
63 self.glove = glove
64 self.helmet = helmet
65 self.ring = ring
66 self.weapon = weapon
67 }
68 }
69
70 access(all) resource NFT: NonFungibleToken.NFT {
71 access(all) let id: UInt64
72 access(all) let svg: String
73 access(all) var winCount: UInt64
74 access(all) var rarityScore: UInt64
75
76 init(id: UInt64, svg: String, rarityScore: UInt64) {
77 self.id = id
78 self.svg = svg
79 self.rarityScore = rarityScore
80 self.winCount = 0
81 }
82
83 access(all) view fun getSVG(): String {
84 return self.svg
85 }
86
87 access(all) view fun getWinCount(): UInt64 {
88 return self.winCount
89 }
90
91 access(all) view fun getRarityScore(): UInt64 {
92 return self.rarityScore
93 }
94
95 access(all) fun updateRarityScore(newScore: UInt64) {
96 self.rarityScore = newScore
97 }
98
99 access(all) fun incrementWinCount() {
100 self.winCount = self.winCount + 1
101 }
102
103 access(all) view fun getViews(): [Type] {
104 return [
105 Type<MetadataViews.Display>(),
106 Type<MetadataViews.ExternalURL>(),
107 Type<MetadataViews.Traits>(),
108 Type<MetadataViews.Royalties>(),
109 Type<MetadataViews.NFTView>(),
110 Type<MetadataViews.NFTCollectionData>(),
111 Type<MetadataViews.NFTCollectionDisplay>()
112 ]
113 }
114
115 access(all) fun resolveView(_ view: Type): AnyStruct? {
116 switch view {
117 case Type<MetadataViews.Display>():
118 return MetadataViews.Display(
119 name: "Bag # ".concat(self.id.toString()),
120 description: "Bag (OCB) is a fully on-chain GameFi asset built on the Flow blockchain, powered by Flow VRF randomness to ensure complete fairness. Each Bag contains 7 unique traits, forming a one-of-a-kind warrior identity. Bag is not just about art — every mint amount is staked into Flow nodes, generating real yield that flows back to holders. It's identity, utility, and rewards, all packed into a single Bag.",
121 thumbnail: MetadataViews.HTTPFile(url: self.getSVG())
122 )
123 case Type<MetadataViews.ExternalURL>():
124 return MetadataViews.ExternalURL("https://onchainbag.xyz")
125 case Type<MetadataViews.Traits>():
126 return Bag.resolveNFTTraits(nftId: self.id)
127 case Type<MetadataViews.Royalties>():
128 let owner = getAccount(Bag.owner)
129 let cut = MetadataViews.Royalty(
130 receiver: owner.capabilities.get<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver),
131 cut: 0.05, // 5% royalty
132 description: "Creator Royalty"
133 )
134 var royalties: [MetadataViews.Royalty] = [cut]
135 return MetadataViews.Royalties(royalties)
136 case Type<MetadataViews.NFTView>():
137 let display = self.resolveView(Type<MetadataViews.Display>())! as! MetadataViews.Display
138 let externalURL = self.resolveView(Type<MetadataViews.ExternalURL>())! as! MetadataViews.ExternalURL
139 let collectionData = Bag.resolveContractView(
140 resourceType: self.getType(),
141 viewType: Type<MetadataViews.NFTCollectionData>()
142 )! as! MetadataViews.NFTCollectionData
143 let collectionDisplay = Bag.resolveContractView(
144 resourceType: self.getType(),
145 viewType: Type<MetadataViews.NFTCollectionDisplay>()
146 )! as! MetadataViews.NFTCollectionDisplay
147 let royalties = self.resolveView(Type<MetadataViews.Royalties>())! as! MetadataViews.Royalties
148 let traits = self.resolveView(Type<MetadataViews.Traits>())! as! MetadataViews.Traits
149 return MetadataViews.NFTView(
150 id: self.id,
151 uuid: self.uuid,
152 display: display,
153 externalURL: externalURL,
154 collectionData: collectionData,
155 collectionDisplay: collectionDisplay,
156 royalties: royalties,
157 traits: traits
158 )
159 case Type<MetadataViews.NFTCollectionData>():
160 return Bag.resolveContractView(resourceType: Type<@NFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
161 case Type<MetadataViews.NFTCollectionDisplay>():
162 return Bag.resolveContractView(resourceType: Type<@NFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>())
163 default:
164 return nil
165 }
166 }
167
168 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
169 return <- Bag.createEmptyCollection(nftType: Type<@Bag.NFT>())
170 }
171 }
172
173 access(all) resource interface CollectionPublic {
174 access(all) view fun getLength(): Int
175 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
176 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?
177 access(all) fun forEachID(_ f: fun (UInt64): Bool): Void
178 access(all) fun deposit(token: @{NonFungibleToken.NFT})
179 access(all) fun borrowBagNFT(id: UInt64): &Bag.NFT? {
180 post {
181 (result == nil) || (result?.id == id):
182 "Cannot borrow Bag reference: The ID of the returned reference is incorrect"
183 }
184 }
185 }
186 access(all) resource Collection: CollectionPublic, NonFungibleToken.Collection {
187 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
188
189 init() {
190 self.ownedNFTs <- {}
191 }
192
193 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
194 pre {
195 self.ownedNFTs.containsKey(withdrawID):
196 "NFT with ID ".concat(withdrawID.toString()).concat(" not found in collection")
197 }
198
199 let owner = self.owner?.address!
200 let token <- self.ownedNFTs.remove(key: withdrawID)!
201 let id = token.id
202
203 // Call BagRegistry to unregister holder
204 if let ownerAddress = self.owner?.address {
205 let registryRef = getAccount(Bag.registryAddress).contracts.borrow<&BagRegistry>(name: "BagRegistry")
206 let _ = registryRef?.onNFTWithdrawn(bagId: id, from: owner)!
207 }
208 emit NFTWithdrawn(id: id, from: owner)
209 return <- token
210 }
211
212 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
213 return {Type<@Bag.NFT>(): true}
214 }
215
216 access(all) view fun isSupportedNFTType(type: Type): Bool {
217 return self.getSupportedNFTTypes()[type] ?? false
218 }
219
220 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
221 pre {
222 self.isSupportedNFTType(type: token.getType()):
223 "Unsupported NFT type: ".concat(token.getType().identifier)
224 self.ownedNFTs[token.id] == nil:
225 "NFT with ID ".concat(token.id.toString()).concat(" already exists in collection")
226 }
227
228 let token <- token as! @Bag.NFT
229 let id = token.id
230 let existingToken <- self.ownedNFTs[id] <- token
231 emit NFTDeposited(id: id, to: self.owner?.address)
232 destroy existingToken
233 // Call BagRegistry to register holder
234 if let ownerAddress = self.owner?.address {
235 let registryRef = getAccount(Bag.registryAddress).contracts.borrow<&BagRegistry>(name: "BagRegistry")
236 let _ = registryRef?.onNFTDeposited(bagId: id, to: ownerAddress)!
237 }
238 }
239
240 access(all) view fun getCollectionSize(): Int {
241 return self.ownedNFTs.length
242 }
243
244 access(all) fun forEachNFTId(_ callback: fun (UInt64): Bool) {
245 self.ownedNFTs.forEachKey(callback)
246 }
247
248 access(all) view fun getIDs(): [UInt64] {
249 return self.ownedNFTs.keys
250 }
251
252 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
253 return &self.ownedNFTs[id]
254 }
255
256 access(all) fun borrowBagNFT(id: UInt64): &Bag.NFT? {
257 if self.ownedNFTs[id] != nil {
258 let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
259 let bagNFT = ref as! &Bag.NFT
260 return bagNFT
261 }else{
262 return nil
263 }
264 }
265
266 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection}{
267 return <- Bag.createEmptyCollection(nftType: Type<@Bag.NFT>())
268 }
269 }
270
271 access(all) resource Admin {
272 access(all) fun mintReservedNFT(recipient: Address): @Bag.NFT {
273 pre {
274 Bag.reservedMinted < Bag.reservedSupply :
275 "No reserved NFTs available. Reserved supply: ".concat(Bag.reservedSupply.toString())
276 recipient == Bag.owner: "Only contract owner can mint reserved NFTs"
277 }
278
279 Bag.totalSupply = Bag.totalSupply + 1
280 Bag.reservedMinted = Bag.reservedMinted + 1
281
282 let id = Bag.totalSupply
283 let svg = Bag.createSVG()
284 let rarityScore = Bag.bagRarityScores[id]
285 ?? panic("Rarity score not found for NFT ID: ".concat(id.toString()))
286
287 var newNFT <- create NFT(id: id, svg: svg, rarityScore: rarityScore)
288 emit NFTMinted(id: newNFT.id, svg: newNFT.svg, mintedFor: recipient)
289
290 return <- newNFT
291 }
292 }
293
294 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
295 return <- create Collection()
296 }
297
298 access(all) fun borrowNFT(ownerAddress: Address, nftId: UInt64): &Bag.NFT {
299 let collectionRef = getAccount(ownerAddress)
300 .capabilities
301 .borrow<&Bag.Collection>(Bag.CollectionPublicPath)
302 ?? panic("Cannot borrow collection reference from address: ".concat(ownerAddress.toString()))
303
304 let nftRef = collectionRef.borrowBagNFT(id: nftId)
305 ?? panic("NFT not found with ID: ".concat(nftId.toString()))
306
307 return nftRef
308 }
309
310 access(self) fun getRandomBackground(): String {
311 let index = Bag.generateRandomIndex(upperBound: Background.backgrounds.length)
312 return Background.backgrounds[index]
313 }
314
315 access(self) fun getRandomBody(): String {
316 let index = Bag.generateRandomIndex(upperBound: Body.body.length)
317 return Body.body[index]
318 }
319
320 access(self) fun getRandomCloth(): String {
321 let index = Bag.generateRandomIndex(upperBound: Cloth.cloths.length)
322 return Cloth.cloths[index]
323 }
324
325 access(self) fun getRandomWeapon(): String {
326 let index = Bag.generateRandomIndex(upperBound: Weapon.weapons.length)
327 return Weapon.weapons[index]
328 }
329
330 access(self) fun getRandomGlove(): String {
331 let index = Bag.generateRandomIndex(upperBound: Glove.gloves.length)
332 return Glove.gloves[index]
333 }
334
335 access(self) fun getRandomRing(): String {
336 let index = Bag.generateRandomIndex(upperBound: Ring.rings.length)
337 return Ring.rings[index]
338 }
339
340 access(self) fun getRandomHelmet(): String {
341 let index = Bag.generateRandomIndex(upperBound: Helmet.helmets.length)
342 return Helmet.helmets[index]
343 }
344
345 access(self) fun generateRandomIndex(upperBound: Int): Int {
346 assert(upperBound > 0, message: "Upper bound must be greater than 0")
347 let randomValue: UInt64 = revertibleRandom<UInt64>()
348 return Int(randomValue % UInt64(upperBound))
349 }
350
351 access(self) fun getRarityScoreValue(rarity: String): UInt64 {
352 switch rarity {
353 case "Common": return 1
354 case "Rare": return 2
355 case "Epic": return 3
356 case "Legendary": return 5
357 default:
358 panic("Unknown rarity type: ".concat(rarity))
359 }
360 }
361
362 access(self) fun calculateRarityScore(itemName: String): UInt64 {
363 let rarity = Rarity.rarity[itemName] ?? "Common"
364 return Bag.getRarityScoreValue(rarity: rarity)
365 }
366
367 access(self) fun generateSVG(): String {
368 var totalRarityScore: UInt64 = 0
369 let bagId = Bag.totalSupply
370
371 var svgContent = "<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMinYMin meet' viewBox='0 0 350 350'>"
372 svgContent = svgContent.concat("<style>.bag { fill: white; font-family: serif; font-size: 14px; font-weight: bold} .base { fill: white; font-family: serif; font-size: 14px; } .title { fill: #ddd; font-family: Bookman; font-size: 10px; text-anchor: middle; }</style>")
373 svgContent = svgContent.concat("<rect width='100%' height='100%' fill='black' />")
374 svgContent = svgContent.concat("<text x='175' y='340' class='title'>build-on-flow</text>")
375 svgContent = svgContent.concat("<text x='10' y='20' class='bag'>bag #".concat(bagId.toString()).concat("</text>"))
376
377 var background: String = ""
378 var body: String = ""
379 var cloth: String = ""
380 var weapon: String = ""
381 var glove: String = ""
382 var ring: String = ""
383 var helmet: String = ""
384 var traitsKey: String = ""
385
386 var attempts = 0
387 let maxAttempts = 100
388
389 while attempts < maxAttempts {
390 attempts = attempts + 1
391
392 background = Bag.getRandomBackground()
393 body = Bag.getRandomBody()
394 cloth = Bag.getRandomCloth()
395 weapon = Bag.getRandomWeapon()
396 glove = Bag.getRandomGlove()
397 ring = Bag.getRandomRing()
398 helmet = Bag.getRandomHelmet()
399
400 traitsKey = Bag.generateTraitsKey(
401 background: background,
402 body: body,
403 cloth: cloth,
404 weapon: weapon,
405 glove: glove,
406 ring: ring,
407 helmet: helmet
408 )
409
410 if !self.uniqueTraitsCombinations.contains(traitsKey) {
411 break
412 }
413
414 if attempts == maxAttempts {
415 panic("Failed to generate unique traits combination after ".concat(maxAttempts.toString()).concat(" attempts"))
416 }
417 }
418
419 self.uniqueTraitsCombinations.append(traitsKey)
420
421 // Add traits to SVG
422 let traits = [background, body, cloth, weapon, glove, ring, helmet]
423 var yPosition = 60
424
425 for trait in traits {
426 svgContent = svgContent.concat("<text x='10' y='".concat(yPosition.toString()).concat("' class='base'>").concat(trait).concat("</text>"))
427 yPosition = yPosition + 20
428 totalRarityScore = totalRarityScore + self.calculateRarityScore(itemName: trait)
429 }
430
431 // Store traits details
432 self.traitsDetails[bagId] = Bag.TraitsDetails(background:background, body:body, cloth:cloth, glove:glove, helmet:helmet, ring:ring, weapon:weapon)
433 self.bagRarityScores[bagId] = totalRarityScore
434
435 // Add rarity score
436 svgContent = svgContent.concat("<text x='10' y='250' class='base'>rarity score: ".concat(totalRarityScore.toString()).concat("</text>"))
437 svgContent = svgContent.concat("</svg>")
438
439 return svgContent
440 }
441
442 access(self) fun generateTraitsKey(background: String, body: String, cloth: String, weapon: String, glove: String, ring: String, helmet: String): String {
443 return background.concat("|")
444 .concat(body).concat("|")
445 .concat(cloth).concat("|")
446 .concat(weapon).concat("|")
447 .concat(glove).concat("|")
448 .concat(ring).concat("|")
449 .concat(helmet)
450 }
451
452 access(self) fun createSVG(): String {
453 let svgImage = Bag.generateSVG()
454 let base64SVG = Base64Util.encode(svgImage)
455 return "data:image/svg+xml;base64,".concat(base64SVG)
456 }
457
458 access(all) fun mintNFT(user:Address, payment: @FlowToken.Vault): @Bag.NFT {
459 pre {
460 self.totalSupply < (self.maxSupply - self.reservedSupply):
461 "Maximum supply reached. Total supply: ".concat(self.totalSupply.toString())
462 payment.balance >= self.mintPrice:
463 "Insufficient payment. Required: ".concat(self.mintPrice.toString()).concat(", Provided: ").concat(payment.balance.toString())
464 self.getCollectionLength(user: user) < 20 : "Maximum 20 Bag per account"
465 }
466
467 let contractReceiver = self.account.capabilities
468 .borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
469 ?? panic("Contract Flow token receiver capability not found")
470
471 contractReceiver.deposit(from: <- payment)
472
473 Bag.totalSupply = Bag.totalSupply + 1
474 let id = Bag.totalSupply
475 let svg = Bag.createSVG()
476
477 let rarityScore = self.bagRarityScores[id] ?? panic("Rarity score not found for NFT ID: ".concat(id.toString()))
478
479 var newNFT <- create NFT(id: id, svg: svg, rarityScore: rarityScore)
480 emit NFTMinted(id: newNFT.id, svg: newNFT.svg, mintedFor:user)
481
482 if(Bag.totalSupply % Bag.priceUpdateInterval == 0 && Bag.totalSupply > 0){
483 self.updatePrice()
484 }
485
486 return <- newNFT
487 }
488
489 access(self) fun updatePrice() {
490 let oldPrice = Bag.mintPrice
491 Bag.mintPrice = Bag.mintPrice + Bag.priceIncreaseAmount
492 emit PriceUpdated(oldPrice: oldPrice, newPrice: Bag.mintPrice)
493 }
494
495 access(all) fun resolveNFTTraits(nftId: UInt64): MetadataViews.Traits? {
496 if let traits = self.traitsDetails[nftId] {
497 let metadata: {String: AnyStruct} = {
498 "Background": traits.background,
499 "Body": traits.body,
500 "Cloth": traits.cloth,
501 "Glove": traits.glove,
502 "Helmet": traits.helmet,
503 "Ring": traits.ring,
504 "Weapon": traits.weapon
505 }
506 return MetadataViews.dictToTraits(dict: metadata, excludedNames: [])
507 }
508 return nil
509 }
510
511 access(all) view fun getContractViews(resourceType: Type?): [Type] {
512 return [
513 Type<MetadataViews.NFTCollectionData>(),
514 Type<MetadataViews.NFTCollectionDisplay>(),
515 Type<MetadataViews.Royalties>()
516 ]
517 }
518
519 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
520 switch viewType {
521 case Type<MetadataViews.NFTCollectionData>():
522 return MetadataViews.NFTCollectionData(
523 storagePath: Bag.CollectionStoragePath,
524 publicPath: Bag.CollectionPublicPath,
525 publicCollection: Type<&Collection>(),
526 publicLinkedType: Type<&Collection>(),
527 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
528 return <-Bag.createEmptyCollection(nftType: Type<@Bag.NFT>())
529 })
530 )
531 case Type<MetadataViews.NFTCollectionDisplay>():
532 let media = MetadataViews.Media(
533 file: MetadataViews.HTTPFile(
534 url: "https://white-worldwide-unicorn-392.mypinata.cloud/ipfs/bafybeigeadg24nqk5vuxjrcuv7w5p6ujxtzv3rilpigwh3c5wbmk2utyva"
535 ),
536 mediaType: "image/png"
537 )
538 let mediaBanner = MetadataViews.Media(
539 file: MetadataViews.HTTPFile(
540 url: "https://white-worldwide-unicorn-392.mypinata.cloud/ipfs/bafybeihoztqdrbkkdmb3jmo7yrx4kdcamq7hr5ohsuq3ks3kmpfo3yrwxm"
541 ),
542 mediaType: "image/png"
543 )
544 return MetadataViews.NFTCollectionDisplay(
545 name: "Bag Collection",
546 description: "Own a Bag & get the chance to win weekly rewards and more",
547 externalURL: MetadataViews.ExternalURL("https://onchainbag.xyz"),
548 squareImage: media,
549 bannerImage: mediaBanner,
550 socials: {"twitter": MetadataViews.ExternalURL("https://x.com/onchainbag")}
551 )
552 case Type<MetadataViews.Royalties>():
553 return MetadataViews.Royalties([])
554 default:
555 return nil
556 }
557 }
558
559 /* --- View Functions --- */
560 access(all) view fun getFlowBalance(): UFix64 {
561 let vaultRef = self.account.capabilities
562 .borrow<&FlowToken.Vault>(/public/flowTokenBalance)
563 ?? panic("Flow token vault not found at /public/flowTokenBalance")
564
565 return vaultRef.balance
566 }
567
568 access(all) view fun getMintPrice(): UFix64 {
569 return self.mintPrice
570 }
571
572 access(all) view fun getTotalSupply(): UInt64 {
573 return self.totalSupply
574 }
575
576 access(all) view fun getMaxSupply(): UInt64 {
577 return self.maxSupply
578 }
579
580 access(all) view fun getReservedSupply(): UInt64 {
581 return self.reservedSupply
582 }
583
584 access(all) view fun getReservedMinted(): UInt64 {
585 return self.reservedMinted
586 }
587
588 access(all) view fun getTraitsDetails(nftId: UInt64): Bag.TraitsDetails? {
589 return self.traitsDetails[nftId]
590 }
591
592 access(all) view fun getBagRarityScore(nftId: UInt64): UInt64? {
593 return self.bagRarityScores[nftId]
594 }
595
596 access(all) view fun hasCollection(user: Address): Bool {
597 let account = getAccount(user)
598 return account.capabilities.get<&Bag.Collection>(Bag.CollectionPublicPath).check()
599 }
600
601 access(all) view fun getCollectionRef(user:Address): &Bag.Collection{
602 return getAccount(user).capabilities.get<&Bag.Collection>(Bag.CollectionPublicPath).borrow()?? panic("Cannot borrow collection reference")
603 }
604
605 access(all) view fun getCollectionLength(user: Address): Int {
606 pre {
607 self.hasCollection(user: user): "User does not have a Bag collection"
608 }
609
610 return self.getCollectionRef(user:user).getIDs().length
611 }
612
613 access(all) view fun getCollectionNFTIds(user: Address): [UInt64] {
614 pre {
615 self.hasCollection(user: user):
616 "User does not have a Bag collection"
617 }
618
619 return self.getCollectionRef(user:user).getIDs()
620 }
621
622 access(all) fun getNFTWinCount(ownerAddress: Address, nftId: UInt64): UInt64 {
623 let nft = Bag.borrowNFT(ownerAddress: ownerAddress, nftId: nftId)
624 return nft.winCount
625 }
626
627 access(all) fun getNFTRarityScore(ownerAddress: Address, nftId: UInt64): UInt64 {
628 let nft = Bag.borrowNFT(ownerAddress: ownerAddress, nftId: nftId)
629 return nft.rarityScore
630 }
631
632 init(owner: Address, mintPrice:UFix64, reserveSupply:UInt64, registryAddress:Address) {
633 self.totalSupply = 0
634 self.maxSupply = 7777
635 self.mintPrice = mintPrice
636 self.traitsDetails = {}
637 self.bagRarityScores = {}
638 self.uniqueTraitsCombinations = []
639
640 self.owner = owner
641 self.registryAddress = registryAddress
642 self.reservedSupply = reserveSupply
643 self.reservedMinted = 0
644
645 // After each interval, mint price will increase by 10.0 Flow
646 // Giving benefit to early holders
647 self.priceUpdateInterval = 555
648 self.priceIncreaseAmount = 10.0
649
650 self.CollectionStoragePath = /storage/BagCollections
651 self.CollectionPublicPath = /public/BagCollectionPublics
652 self.CollectionPrivatePath = /private/BagCollectionProviders
653 self.AdminStoragePath = /storage/BagAdmins
654
655 let collection <- create Collection()
656 self.account.storage.save(<- collection, to: self.CollectionStoragePath)
657
658 let admin <- create Admin()
659 self.account.storage.save(<- admin, to: self.AdminStoragePath)
660
661 let collectionCapability = self.account.capabilities.storage
662 .issue<&Bag.Collection>(Bag.CollectionStoragePath)
663 self.account.capabilities.publish(collectionCapability, at: self.CollectionPublicPath)
664
665 emit ContractInitialized()
666 }
667}