Smart Contract

StoreManagerV3

A.4bbff461fa8f6192.StoreManagerV3

Deployed

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

Dependents

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