Smart Contract

StoreManagerV5

A.4bbff461fa8f6192.StoreManagerV5

Deployed

14h ago
Feb 28, 2026, 02:27:40 AM UTC

Dependents

0 imports
1import FantastecSwapDataProperties from 0x4bbff461fa8f6192
2import StoreManagerV3 from 0x4bbff461fa8f6192
3
4access(all) contract StoreManagerV5 {
5    access(all) entitlement Manage
6
7    access(all) event SectionAdded(id: UInt64)
8    access(all) event SectionRemoved(id: UInt64)
9
10    access(all) event ProductAdded(id: UInt64)
11    access(all) event ProductRemoved(id: UInt64)
12    access(all) event ProductUpdated(id: UInt64)
13    access(all) event ProductVolumeForSaleDecremented(id: UInt64, newVolumeForSale: UInt64)
14
15    access(all) event SectionItemAdded(id: UInt64, sectionId: UInt64, productId: UInt64)
16    access(all) event SectionItemRemoved(id: UInt64, sectionId: UInt64)
17
18    access(all) event ContractInitiliazed()
19
20    access(all) let StoreManagerDataPath: StoragePath
21
22    access(contract) var nextSectionItemId: UInt64
23    access(contract) var nextSectionId: UInt64
24
25    access(all) struct Product {
26        access(all) let id: UInt64
27        access(all) let description: String
28        access(all) let level: FantastecSwapDataProperties.Level?
29        access(all) let numberOfOptionalNfts: UInt64
30        access(all) let numberOfPacks: UInt64
31        access(all) let numberOfRegularNfts: UInt64
32        access(all) let partner: FantastecSwapDataProperties.Partner?
33        access(all) let season: FantastecSwapDataProperties.Season?
34        access(all) let shortTitle: String
35        access(all) let sku: FantastecSwapDataProperties.Sku?
36        access(all) let sport: FantastecSwapDataProperties.Sport?
37        access(all) let team: FantastecSwapDataProperties.Team?
38        access(all) let themeType: String
39        access(all) let title: String
40        access(all) let releaseDate: UFix64
41        access(all) var volumeForSale: UInt64
42        access(all) let productImageUrl: String
43        access(all) let productVideoUrl: String
44        access(all) let backgroundImageSmallUrl: String
45        access(all) let backgroundImageLargeUrl: String
46        access(all) let featuredImageUrl: String
47        access(all) let featuredVideoUrl: String
48        access(all) var metadata: {String: [{FantastecSwapDataProperties.MetadataElement}]}
49
50        init(
51            id: UInt64,
52            description: String,
53            level: FantastecSwapDataProperties.Level?,
54            numberOfOptionalNfts: UInt64,
55            numberOfPacks: UInt64,
56            numberOfRegularNfts: UInt64,
57            partner: FantastecSwapDataProperties.Partner?,
58            season: FantastecSwapDataProperties.Season?,
59            shortTitle: String,
60            sku: FantastecSwapDataProperties.Sku?,
61            sport: FantastecSwapDataProperties.Sport?,
62            team: FantastecSwapDataProperties.Team?,
63            themeType: String,
64            title: String,
65            releaseDate: UFix64,
66            volumeForSale: UInt64,
67            productImageUrl: String,
68            productVideoUrl: String,
69            backgroundImageSmallUrl: String,
70            backgroundImageLargeUrl: String,
71            featuredImageUrl: String,
72            featuredVideoUrl: String,
73            metadata: {String: [{FantastecSwapDataProperties.MetadataElement}]}
74        ){
75            self.id = id
76            self.description = description
77            self.level = level
78            self.numberOfOptionalNfts = numberOfOptionalNfts
79            self.numberOfPacks = numberOfPacks
80            self.numberOfRegularNfts = numberOfRegularNfts
81            self.partner = partner
82            self.season = season
83            self.shortTitle = shortTitle
84            self.sku = sku
85            self.sport = sport
86            self.team = team
87            self.themeType = themeType
88            self.title = title
89            self.releaseDate = releaseDate
90            self.volumeForSale = volumeForSale
91            self.productImageUrl = productImageUrl
92            self.productVideoUrl = productVideoUrl
93            self.backgroundImageSmallUrl = backgroundImageSmallUrl
94            self.backgroundImageLargeUrl = backgroundImageLargeUrl
95            self.featuredImageUrl = featuredImageUrl
96            self.featuredVideoUrl = featuredVideoUrl
97            self.metadata = metadata
98        }
99
100        access(contract) fun decrementProductVolumeForSale(): UInt64 {
101            if self.volumeForSale == 0 {
102                panic("cannot decrement product for sale as volume for sale is zero - product ID: ".concat(self.id.toString()))
103            }
104            self.volumeForSale = self.volumeForSale - 1
105            return self.volumeForSale
106        }
107    }
108
109    access(all) struct SectionItem {
110        access(all) let id: UInt64
111        access(all) let position: UInt64
112        access(all) var product: Product?
113        access(all) let productId: UInt64
114
115        init(id: UInt64, position: UInt64, productId: UInt64) {
116            self.id = id
117            self.position = position
118            self.product = nil
119            self.productId = productId
120        }
121
122        access(Manage) fun addProduct(product: Product) {
123            self.product = product
124        }
125    }
126
127    access(all) struct Section {
128        access(all) let id: UInt64
129        access(all) let sectionItems: {UInt64: SectionItem}
130        access(all) let position: UInt64
131        access(all) let title: String
132        access(all) let type: String
133
134        init(id: UInt64, position: UInt64, title: String, type: String) {
135            self.id = id
136            self.sectionItems = {}
137            self.position = position
138            self.title = title
139            self.type = type
140        }
141
142        access(Manage) fun addSectionItem(position: UInt64, productId: UInt64): SectionItem {
143            let id = StoreManagerV5.nextSectionItemId
144
145            let sectionItem = SectionItem(id: id, position: position, productId: productId)
146            self.sectionItems.insert(key: id, sectionItem)
147
148            StoreManagerV5.nextSectionItemId = StoreManagerV5.nextSectionItemId + 1
149
150            return sectionItem
151        }
152
153        access(Manage) fun addProductToSectionItem(sectionItemId: UInt64, product: Product): SectionItem? {
154            if let sectionItem = self.sectionItems[sectionItemId] {
155                sectionItem.addProduct(product: product)
156                self.sectionItems[sectionItemId] = sectionItem
157                return sectionItem
158            }
159            return nil
160        }
161
162        access(Manage) fun removeSectionItem(id: UInt64): SectionItem? {
163            return self.sectionItems.remove(key: id)
164        }
165    }
166    
167    //-------------------------
168    // Contract level functions
169    //-------------------------
170    access(all) fun getStore(): {UInt64: Section} {
171        return self.getDataManager().getStore()
172    }
173
174    access(all) fun getProduct(productId: UInt64): Product? {
175        return self.getDataManager().getProduct(productId: productId)
176    }
177
178    access(all) fun getProducts(productIds: [UInt64]): [Product] {
179        return self.getDataManager().getProducts(productIds: productIds)
180    }
181
182    access(all) fun getAllProducts(): {UInt64: Product} {
183        return self.getDataManager().getAllProducts()
184    }
185
186    access(all) resource DataManager {
187        access(contract) let products: {UInt64: Product}
188        access(contract) let store: {UInt64: Section}
189
190        access(all) fun getStore(): {UInt64: Section} {
191            let products = self.products
192            let store = self.store
193            let currentBlock = getCurrentBlock()
194
195            store.forEachKey(fun (sectionId: UInt64): Bool {
196                if let section = store[sectionId] {
197                    section.sectionItems.forEachKey(fun (sectionItemId: UInt64): Bool {
198                        if let sectionItem = section.sectionItems[sectionItemId] {
199                            if let product = products[sectionItem.productId] {
200                                if product.releaseDate <= currentBlock.timestamp {
201                                    section.addProductToSectionItem(sectionItemId: sectionItem.id, product: product)
202                                }
203                            }
204                        }
205
206                        return true
207                    })
208
209                    store[sectionId] = section
210                }
211
212                return true
213            })
214
215            return store
216        }
217
218        access(Manage) fun addProduct(
219            id: UInt64,
220            description: String,
221            level: FantastecSwapDataProperties.Level?,
222            numberOfOptionalNfts: UInt64,
223            numberOfPacks: UInt64,
224            numberOfRegularNfts: UInt64,
225            partner: FantastecSwapDataProperties.Partner?,
226            season: FantastecSwapDataProperties.Season?,
227            shortTitle: String,
228            sku: FantastecSwapDataProperties.Sku?,
229            sport: FantastecSwapDataProperties.Sport?,
230            team: FantastecSwapDataProperties.Team?,
231            themeType: String,
232            title: String,
233            releaseDate: UFix64,
234            volumeForSale: UInt64,
235            productImageUrl: String,
236            productVideoUrl: String,
237            backgroundImageSmallUrl: String,
238            backgroundImageLargeUrl: String,
239            featuredImageUrl: String,
240            featuredVideoUrl: String,
241            metadata: {String: [{FantastecSwapDataProperties.MetadataElement}]}
242        ) {
243            let product = Product(
244                id: id,
245                description: description,
246                level: level,
247                numberOfOptionalNfts: numberOfOptionalNfts,
248                numberOfPacks: numberOfPacks,
249                numberOfRegularNfts: numberOfRegularNfts,
250                partner: partner,
251                season: season,
252                shortTitle: shortTitle,
253                sku: sku,
254                sport: sport,
255                team: team,
256                themeType: themeType,
257                title: title,
258                releaseDate: releaseDate,
259                volumeForSale: volumeForSale,
260                productImageUrl: productImageUrl,
261                productVideoUrl: productVideoUrl,
262                backgroundImageSmallUrl: backgroundImageSmallUrl,
263                backgroundImageLargeUrl: backgroundImageLargeUrl,
264                featuredImageUrl: featuredImageUrl,
265                featuredVideoUrl: featuredVideoUrl,
266                metadata: metadata
267            )            
268            self.products[product.id] = product
269            emit ProductAdded(id: product.id)
270        }
271
272        access(Manage) fun decrementProductVolumeForSale(productId: UInt64) {
273            let newVolumeForSale = self.products[productId]?.decrementProductVolumeForSale()
274            if (newVolumeForSale != nil) {
275                emit ProductVolumeForSaleDecremented(id: productId, newVolumeForSale: newVolumeForSale!)
276            }
277        }
278
279        access(all) fun getProduct(productId: UInt64): Product? {
280            return self.products[productId]
281        }
282
283        access(all) fun getProducts(productIds: [UInt64]): [Product] {
284            let products: [Product] = []
285            for productId in productIds {
286                let product = self.getProduct(productId: productId)
287                if (product != nil) {
288                    products.append(product!)
289                }
290            }
291            return products
292        }
293
294        access(all) fun getAllProducts(): {UInt64: Product} {
295            return self.products
296        }
297
298        access(Manage) fun removeProduct(productId: UInt64) {
299            let store = self.store
300            let sectionItemsToRemove: {UInt64: UInt64} = {}
301            store.forEachKey(fun (sectionId: UInt64): Bool {
302                if let section = store[sectionId] {
303                    section.sectionItems.forEachKey(fun (sectionItemId: UInt64): Bool {
304                        if let sectionItem = section.sectionItems[sectionItemId] {
305                            if sectionItem.productId == productId {
306                                sectionItemsToRemove[section.id] = sectionItem.id
307                            }
308                        }
309
310                        return true
311                    })
312                }
313
314                return true
315            })
316
317            // Remove all section items that are associated with a removed product
318            for sectionId in sectionItemsToRemove.keys {
319                let sectionItemId = sectionItemsToRemove[sectionId]
320                self.removeSectionItemFromSection(sectionId: sectionId, sectionItemId: sectionItemId!)
321                // if the section is now empty, remove it
322                let updatedSection = store[sectionId]
323                if (updatedSection!.sectionItems.length == 0) {
324                    self.removeSection(sectionId: sectionId)
325                }
326            }
327
328            self.products.remove(key: productId)
329            emit ProductUpdated(id: productId)
330        }
331
332        access(Manage) fun addSection(position: UInt64, title: String, type: String): UInt64 {
333            let id = StoreManagerV5.nextSectionId
334            let section = Section(id: id, position: position, title: title, type: type)
335
336            self.store[section.id] = section
337
338            emit SectionAdded(id: id)
339            StoreManagerV5.nextSectionId = StoreManagerV5.nextSectionId + 1
340
341            return id
342        }
343
344        access(all) fun getSection(sectionId: UInt64): Section? {
345            return self.store[sectionId]
346        }
347
348        access(Manage) fun removeSection(sectionId: UInt64) {
349            self.store.remove(key: sectionId)
350            emit SectionRemoved(id: sectionId)
351        }
352
353        access(Manage) fun addSectionItemToSection(sectionId: UInt64, position: UInt64, productId: UInt64): UInt64 {
354            let sectionItem = self.store[sectionId]?.addSectionItem(position: position, productId: productId)
355            sectionItem ?? panic("no section found with ID ".concat(sectionId.toString()))
356            emit SectionItemAdded(id: sectionItem!.id, sectionId: sectionId, productId: productId)
357
358            return sectionItem!.id
359        }
360
361        access(Manage) fun removeSectionItemFromSection(sectionId: UInt64, sectionItemId: UInt64) {
362            let sectionItem = self.store[sectionId]?.removeSectionItem(id: sectionItemId)
363            sectionItem ?? panic("no section found with ID ".concat(sectionId.toString()))
364            emit SectionItemRemoved(id: sectionItemId, sectionId: sectionId)
365        }
366
367        init(_ products: {UInt64: Product}, _ sections: {UInt64: Section}) {
368            self.products = products
369            self.store = sections
370        }
371    }
372
373    access(contract) fun setDataManager() {
374        let oldDataManager <- self.account.storage.load<@DataManager>(from: self.StoreManagerDataPath)
375        var oldProducts = oldDataManager?.products ?? {}
376        var oldStore = oldDataManager?.store ?? {}
377        self.account.storage.save<@DataManager>(<- create DataManager(oldProducts, oldStore), to: self.StoreManagerDataPath)
378        destroy oldDataManager
379    }
380
381    access(contract) fun getDataManager(): &DataManager {
382        return self.account.storage.borrow<&DataManager>(from: self.StoreManagerDataPath)!
383    }
384
385    init() {
386        self.nextSectionItemId = 1
387        self.nextSectionId = 1
388
389        self.StoreManagerDataPath = /storage/StoreManagerV5Data
390
391        // self.setDataManager()
392        emit ContractInitiliazed()
393    }
394}
395
396