Smart Contract
StoreManagerV3
A.4bbff461fa8f6192.StoreManagerV3
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