Smart Contract

NFTSalesCapped

A.a49cc0ee46c54bfb.NFTSalesCapped

Deployed

1d ago
Feb 26, 2026, 09:43:52 PM UTC

Dependents

0 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import REVV from 0xd01e482eb680ec9f
3import SHRD from 0xd01e482eb680ec9f
4import MotoGPAdmin from 0xa49cc0ee46c54bfb
5import MotoGPCard from 0xa49cc0ee46c54bfb
6import CardMintAccess from 0xa49cc0ee46c54bfb
7import ContractVersion from 0xa49cc0ee46c54bfb
8import SHRDMintAccess from 0xd01e482eb680ec9f
9import MotoGPCardMetadata from 0xa49cc0ee46c54bfb
10import MotoGPCardSerialPoolV2 from 0xa49cc0ee46c54bfb
11import Pausable from 0xb223b2bfe4b8ffb5
12
13pub contract NFTSalesCapped : ContractVersion {
14
15    pub fun getVersion(): String {
16        return "1.0.8"
17    }
18
19    pub event OrderIdRegistered(orderId: String)
20    pub event PackOpened(cardIDs: [UInt64], serials: [UInt64], shrdAmount: UFix64)
21
22    pub resource OpenedPack{
23        pub var collection: @MotoGPCard.Collection
24        pub var vault: @SHRD.Vault
25        init(collection_: @MotoGPCard.Collection, vault_: @SHRD.Vault) {
26            self.collection <- collection_
27            self.vault <- vault_
28        }
29        destroy() {
30            // Don't allow destruction if not empty
31            assert(self.collection.getIDs().length == 0, message: "OpenedPack's card collection is not empty")
32            assert(self.vault.balance == 0.0, message: "OpenedPack's shrd vault is not empty")
33            destroy self.collection
34            destroy self.vault
35        }
36    }
37
38    pub enum SalesOpenOverride: UInt8 {
39        pub case NoOverride
40        pub case ForceOpen
41        pub case ForceClose
42    }
43
44    pub resource Sales {
45        pub let id: UInt64
46        pub let name: String
47        pub var salesOpenOverride: UInt8 // Used to override the blocktime-based open status, for emergency situations, e.g. block start time doesn't sync with IRL time
48        pub var totalSupply: UInt64
49        pub var maxSHRD: UFix64  //The maximum allowed amount of SHRD which can be minted by this Sales
50        pub var mintedSHRD: UFix64 //Keeps count of how much SHRD has been minted
51        access(self) var shrdPerGrade: {String:UFix64} // grade (rarity, e.g. Legendary) => amount of SHRD
52        pub var cardsPerPack: UInt64 //how many cards will be minted when a pack is opened
53        pub let nonces: {Address: UInt64} //how many packs have been opened for an account
54        pub var price: UFix64 // for use when paying with Vault
55        pub var publicKey: String // used to verify the signature during an open pack call
56        pub var signatureAlgorithm: UInt8 // SignatureAlorithm type is not storable 
57        pub var startTime: UFix64 // start time in unix time stamp seconds (not milliseconds), to be compared to block time. Note: block timestamps are UFix64.
58        pub var endTime: UFix64 // end time in unix time stamp seconds (not milliseconds), to be compared to block time. Note: block timestamps are UFix64.
59        pub var maxPerWallet: UInt64 // max number of cards a buyer can mint
60        pub var cardCountByWallet: {Address: UInt64} // keeps track of how many cards a buyer has minted
61        pub var sold: UInt64 // the number of cards minted from this Sales resource
62        access(self) var cardTypeWeights: [[UInt32]] // A list of bucket card probability weights. Each inner array holds the weights for one bucket
63        access(self) var cardTypes: [[UInt64]] // A list of bucket card types. Each inner array holds the types for one bucket. Needs to be ordered to match cardTypeWeights's order
64        pub let pausableCap: Capability<&{Pausable.PausableExternal}>
65        init(
66            name: String,
67            totalSupply: UInt64,
68            maxSHRD: UFix64,
69            shrdPerGrade: {String: UFix64}, 
70            cardsPerPack: UInt64, 
71            price: UFix64,
72            startTime: UFix64, 
73            endTime: UFix64,
74            maxPerWallet: UInt64,
75            cardTypeWeights: [[UInt32]], // If empty, should be [], not [[],[],[]]
76            cardTypes: [[UInt64]], // If empty, should be [], not [[],[],[]]
77            pausableCap: Capability<&{Pausable.PausableExternal}>
78            ) {
79                pre {
80                    name.length > 0 : "name has zero length"
81                    NFTSalesCapped.isEqual2DArrayLengths(cardTypeWeights, cardTypes) : "inconsistent initial card type weight array lengths"
82                }
83                self.id = self.uuid
84                self.salesOpenOverride = SalesOpenOverride.NoOverride.rawValue
85                self.name = name
86                self.totalSupply = totalSupply
87                self.maxSHRD = maxSHRD
88                self.mintedSHRD = 0.0
89                self.shrdPerGrade = shrdPerGrade
90                self.cardsPerPack = cardsPerPack
91                self.nonces = {}
92                self.price = price
93                self.publicKey  = ""
94                self.signatureAlgorithm = SignatureAlgorithm.ECDSA_P256.rawValue// To convert back to enum, use: SignatureAlgorithm(rawValue: self.signatureAlgorithm)
95                self.startTime = startTime 
96                self.endTime = endTime
97                self.maxPerWallet = maxPerWallet
98                self.cardCountByWallet = {}
99                self.sold = 0
100                self.cardTypeWeights = cardTypeWeights
101                self.cardTypes = cardTypes
102                self.pausableCap = pausableCap
103        }
104
105        access(contract) fun addCardTypeWeights(cardTypeWeights: [UInt32], cardTypes: [UInt64]){
106            pre {
107                cardTypeWeights.length == cardTypes.length : "inconsistent card type weight array lengths"
108            }
109            self.cardTypeWeights.append(cardTypeWeights)
110            self.cardTypes.append(cardTypes)
111        }
112
113        access(contract) fun removeCardTypeWeights(at index: UInt64) {
114            self.cardTypeWeights.remove(at: index)
115            self.cardTypes.remove(at: index)
116        }
117
118        access(contract) fun clearCardTypeWeights() {
119            self.cardTypeWeights = []
120            self.cardTypes = []
121        }
122
123        access(contract) fun setSalesOpenOverride(state: SalesOpenOverride) {
124            self.salesOpenOverride = state.rawValue
125        }
126
127        access(contract) fun setMaxSHRD(maxSHRD: UFix64) {
128            self.maxSHRD = maxSHRD
129        }
130
131        access(contract) fun setTotalSupply(totalSupply: UInt64) {
132            self.totalSupply = totalSupply
133        }
134
135        access(contract) fun setStartTime(startTime: UFix64) {
136            self.startTime = startTime
137        }
138
139        access(contract) fun setEndTime(endTime: UFix64) {
140            self.endTime = endTime
141        }
142
143        access(contract) fun setPublicKey(publicKey: String, signatureAlgorithm: SignatureAlgorithm) {
144            self.publicKey = publicKey 
145            self.signatureAlgorithm = signatureAlgorithm.rawValue 
146        }
147
148        access(contract) fun setMaxPerWallet(maxPerWallet: UInt64) {
149            self.maxPerWallet = maxPerWallet
150        }
151
152        access(contract) fun setCardsPerPack(cardsPerPack: UInt64) {
153            self.cardsPerPack = cardsPerPack
154        }
155
156        access(contract) fun setSHRDPerGrade(shrdPerGrade: {String: UFix64}) {
157            self.shrdPerGrade = shrdPerGrade
158        }
159
160        access(contract) fun setPrice(price: UFix64) {
161            self.price = price
162        }
163
164        pub fun getSHRDPerGrade(): {String: UFix64} {
165            return self.shrdPerGrade
166        }
167      
168        pub fun isOpen(): Bool {
169            if self.salesOpenOverride == SalesOpenOverride.ForceOpen.rawValue {
170                return true
171            }
172            if self.salesOpenOverride == SalesOpenOverride.ForceClose.rawValue {
173                return false
174            }
175
176            let ts = getCurrentBlock().timestamp
177            return self.startTime <= ts && self.endTime >= ts
178        }
179
180        pub fun openPacksWithCreditCardPayment(
181            signature: String,
182            orderId: String,
183            address: Address,
184            quantity: UInt64,
185            hashAlgorithm: HashAlgorithm
186        ): @OpenedPack {
187
188            pre {
189                !self.pausableCap.borrow()!.isPaused(): "Invalid: Sales is paused"
190                NFTSalesCapped.orderIds[orderId] == nil : "orderId already used" 
191            }
192
193            NFTSalesCapped.orderIds[orderId] = getCurrentBlock().height
194
195            let message = address.toString().concat(orderId).concat(self.uuid.toString()).concat(quantity.toString())
196
197            let isValid = NFTSalesCapped.isValidSignature(
198                publicKey: self.publicKey, 
199                signatureAlgorithm: SignatureAlgorithm(rawValue: self.signatureAlgorithm)!,
200                hashAlgorithm: hashAlgorithm,
201                signature: signature, 
202                message: message)
203            if isValid == false {
204                panic("Signature isn't valid")
205            }
206
207            emit OrderIdRegistered(orderId: orderId)
208        
209            let res <- self.openPack(
210                    signature: signature,
211                    address: address,
212                    quantity: quantity,
213                    message: message
214                ) 
215
216            return <- res
217        }
218
219        pub fun openPacksWithVaultPayment(
220            signature: String,
221            revvVault: @REVV.Vault,
222            address: Address,
223            quantity: UInt64,
224            hashAlgorithm: HashAlgorithm 
225        ): @OpenedPack {
226
227            pre {
228                !self.pausableCap.borrow()!.isPaused(): "Invalid: Sales is paused"
229                self.isOpen() : "Sales is not open"
230                revvVault.balance == self.price * UFix64(quantity) : "revvVault balance doesn't match price * quantity"
231            }
232
233            NFTSalesCapped.paymentReceiverCap!.borrow()!.deposit(from: <- revvVault)
234
235            if self.nonces[address] == nil {
236                self.nonces[address] = UInt64(1)
237            } else {
238                self.nonces[address] = self.nonces[address]! + UInt64(1)
239            }
240           
241            let message = self.nonces[address]!.toString().concat(address.toString()).concat(self.uuid.toString())
242
243            let isValid = NFTSalesCapped.isValidSignature(
244                publicKey: self.publicKey, 
245                signatureAlgorithm: SignatureAlgorithm(rawValue: self.signatureAlgorithm)!,
246                hashAlgorithm: hashAlgorithm,
247                signature: signature, 
248                message: message)
249            if isValid == false {
250                panic("Signature isn't valid")
251            }
252
253            let res <- self.openPack(
254                    signature: signature,
255                    address: address,
256                    quantity: quantity,
257                    message: message,
258                )
259
260            return <- res
261        }
262
263        access(contract) fun openPack( // TODO: change to access(self)
264            signature: String,
265            address: Address,
266            quantity: UInt64,
267            message: String
268        ): @OpenedPack {
269
270            if (self.sold + (quantity * self.cardsPerPack)) > self.totalSupply {
271                panic("totalSupply exceeded")
272            }
273
274            // Mint cards
275            let cardIDsAndSerials = self.generateCardIDsAndSerials(signature: signature, quantity: quantity)
276
277            let cardIDs = cardIDsAndSerials[0]
278            let serials = cardIDsAndSerials[1]
279            let collection:@MotoGPCard.Collection <- NFTSalesCapped.mintCards(cardIDs: cardIDs, serials: serials)
280
281            // Mint shrd
282            var shrdAmount = self.calculateSHRDAmount(cardIDs: cardIDs)
283            let vault <- SHRD.createEmptyVault() as! @SHRD.Vault
284
285            if (shrdAmount > 0.0) {
286                vault.deposit(from: <- NFTSalesCapped.mintSHRD(amount: shrdAmount))
287            }
288
289            // Check and update minted card counts
290            self.sold = self.sold + UInt64(cardIDs.length)
291
292            if self.cardCountByWallet[address] == nil {
293                self.cardCountByWallet[address] = 0
294            }
295            let newCountForWallet = self.cardCountByWallet[address]! + UInt64(cardIDs.length)
296            if newCountForWallet > self.maxPerWallet {
297                panic("max cards exceeded for this wallet")
298            }
299            self.cardCountByWallet[address] = newCountForWallet
300
301            emit PackOpened(cardIDs: cardIDs, serials: serials, shrdAmount: shrdAmount)  
302
303            return <- create OpenedPack(collection_: <- collection, vault_: <- vault)       
304        }
305    
306        pub fun getNonce(address: Address): UInt64 {
307            return self.nonces[address] ?? 0 as UInt64
308        }
309
310        access(contract) fun digestToUInt64(digest: [UInt8]): UInt64 {
311            return (UInt64(digest[0]) << UInt64(56)) 
312            + (UInt64(digest[1]) << UInt64(48)) 
313            + (UInt64(digest[2]) << UInt64(40)) 
314            + (UInt64(digest[3]) << UInt64(32)) 
315            + (UInt64(digest[4]) << UInt64(24)) 
316            + (UInt64(digest[5]) << UInt64(16)) 
317            + (UInt64(digest[6]) << UInt64(8)) 
318            + (UInt64(digest[7]))
319        }
320
321        access(contract) fun generateCardIDsAndSerials(signature: String, quantity: UInt64): [[UInt64]; 2] {
322            let res: [[UInt64]; 2] = [[],[]]
323            var index:UInt64 = 0
324            var digest:[UInt8] = signature.decodeHex()
325            let numCards = self.cardsPerPack * quantity
326            let numBuckets = UInt64(self.cardTypeWeights.length)
327
328            while index < numCards {
329                let bucketIndex = index % numBuckets
330                
331                digest = index == 0 ? digest : HashAlgorithm.KECCAK_256.hash(digest)
332                var n = self.digestToUInt64(digest: digest)
333                let cardID = self.generateSingleCardIDFromDigest(n: n, bucketIndex: bucketIndex)    
334                res[0].append(cardID)
335
336                digest = HashAlgorithm.KECCAK_256.hash(digest)
337                n = self.digestToUInt64(digest: digest)
338                res[1].append(MotoGPCardSerialPoolV2.pickSerial(n: n, cardID: cardID))
339
340                index = index + 1
341            }
342            return res
343        }
344
345        access(contract) fun generateSingleCardIDFromDigest(n: UInt64, bucketIndex: UInt64): UInt64 {
346            let cardTypeWeights = self.cardTypeWeights[bucketIndex]
347            let totalWeight = cardTypeWeights[cardTypeWeights.length - 1]
348            let r = n % UInt64(totalWeight)
349            for i, weight in cardTypeWeights {
350                if r <= UInt64(weight) {
351                    return self.cardTypes[bucketIndex][i]
352                }
353            }
354            panic("no cardType matched to weight: ".concat(r.toString().concat(" with totalWeight: ").concat(totalWeight.toString())))
355        }
356
357        access(contract) fun calculateSHRDAmount(cardIDs: [UInt64]): UFix64 {
358            if self.mintedSHRD >= self.maxSHRD {
359                return 0.0   
360            }
361            var res = 0.0
362            for cardID in cardIDs {
363                let metadata = MotoGPCardMetadata.getMetadataForCardID(cardID: cardID) ?? panic("cardID ".concat(cardID.toString()).concat(" has no matching metadata"))
364                let grade = metadata.data["grade"] ?? panic("cardID ".concat(cardID.toString()).concat(" metadata has no grade"))
365                let shrdAmount = self.shrdPerGrade[grade] ?? 0.0
366                res = res + shrdAmount
367            }
368            if res + self.mintedSHRD > self.maxSHRD {
369				res = self.maxSHRD - self.mintedSHRD
370			}
371            self.mintedSHRD = self.mintedSHRD + res
372            return res
373        }
374    }
375
376    pub resource interface SalesCollectionPublic {
377        pub fun getIDs(): [UInt64]
378        pub fun borrowSales(salesID: UInt64): &Sales
379        pub fun getCardCount(salesID: UInt64): UInt64 
380    }
381
382    pub resource SalesCollection: SalesCollectionPublic {
383        
384        pub var salesMap: @{UInt64: Sales}
385
386        pub fun addSales(sales: @Sales) {
387            pre {
388                NFTSalesCapped.isPaymentReceiverCapSet() == true : "payment receiver is not set"
389            }
390            let salesID = sales.id
391            let oldItem <- self.salesMap[salesID] <- sales
392            // TODO: Add event
393            destroy oldItem
394        }
395        pub fun removeSales(salesID: UInt64): @Sales {
396            let sales <- self.salesMap.remove(key: salesID) ?? panic("missing Sales")
397            // TODO: Add event
398            return <- sales
399        }
400
401        pub fun setSalesOpenOverride(salesID: UInt64, state: NFTSalesCapped.SalesOpenOverride) {
402            self.borrowSales(salesID: salesID).setSalesOpenOverride(state: state)
403        }
404        pub fun setTotalSupply(salesID: UInt64, totalSupply: UInt64) {
405            self.borrowSales(salesID: salesID).setTotalSupply(totalSupply: totalSupply)
406        }
407        pub fun setMaxSHRD(salesID: UInt64, maxSHRD: UFix64) {
408            self.borrowSales(salesID: salesID).setMaxSHRD(maxSHRD: maxSHRD)
409        }
410        pub fun setPublicKey(salesID: UInt64, publicKey:String, signatureAlgorithm: SignatureAlgorithm) {
411            self.borrowSales(salesID: salesID).setPublicKey(publicKey: publicKey, signatureAlgorithm: signatureAlgorithm)
412        }
413        pub fun setMaxPerWallet(salesID: UInt64, maxPerWallet: UInt64) {
414            self.borrowSales(salesID: salesID).setMaxPerWallet(maxPerWallet: maxPerWallet)
415        }
416        pub fun setCardPerPack(salesID: UInt64, cardsPerPack: UInt64) {
417            self.borrowSales(salesID: salesID).setCardsPerPack(cardsPerPack: cardsPerPack)
418        }
419        pub fun setStartTime(salesID: UInt64, startTime: UFix64) {
420            self.borrowSales(salesID: salesID).setStartTime(startTime: startTime)
421        }
422        pub fun setEndTime(salesID: UInt64, endTime: UFix64) {
423            self.borrowSales(salesID: salesID).setEndTime(endTime: endTime)
424        }
425        pub fun addCardTypeWeights(salesID: UInt64, cardTypeWeights: [UInt32], cardTypes: [UInt64]) {
426            self.borrowSales(salesID: salesID).addCardTypeWeights(cardTypeWeights: cardTypeWeights, cardTypes: cardTypes)
427        }
428        pub fun setSHRDPerGrade(salesID: UInt64, shrdPerGrade: {String: UFix64}) {
429            self.borrowSales(salesID: salesID).setSHRDPerGrade(shrdPerGrade: shrdPerGrade)
430        }
431        pub fun setPrice(salesID: UInt64, price: UFix64) {
432            self.borrowSales(salesID: salesID).setPrice(price: price)
433        }
434        pub fun removeCardTypeWeights(salesID: UInt64, at index: UInt64) {
435            self.borrowSales(salesID: salesID).removeCardTypeWeights(at: index)
436        }
437        pub fun clearCardTypeWeights(salesID: UInt64) {
438            self.borrowSales(salesID: salesID).clearCardTypeWeights()
439        }
440        pub fun getCardCount(salesID: UInt64): UInt64 {
441            return self.borrowSales(salesID: salesID).sold
442        }
443        pub fun getNonceForSales(salesID: UInt64, address: Address): UInt64 {
444            return self.borrowSales(salesID: salesID).getNonce(address: address)
445        }
446        pub fun getIDs(): [UInt64] {
447            return self.salesMap.keys
448        }
449        pub fun borrowSales(salesID: UInt64): &Sales {
450            return (&self.salesMap[salesID] as &Sales?)!
451        }
452        init() {
453            self.salesMap <- {}
454        }
455        destroy(){
456            destroy self.salesMap
457        }
458    }
459
460    pub fun isSHRDMintProxyCapSet(): Bool {
461        return self.shrdMintProxyCap != nil
462    }
463
464    pub fun isCardMintProxyCapSet(): Bool { 
465        return self.cardMintProxyCap != nil
466    }
467
468    pub fun isPaymentReceiverCapSet(): Bool {
469        return self.paymentReceiverCap != nil
470    }
471
472    pub fun isOrderIdUsed(orderId: String): Bool {
473        return NFTSalesCapped.orderIds[orderId] != nil
474    }
475
476    pub fun getBlockHeightForOrderId(orderId: String): UInt64? {
477        return NFTSalesCapped.orderIds[orderId]
478    }
479
480    pub fun createSales(
481        adminRef: &MotoGPAdmin.Admin, 
482        name: String,
483        totalSupply: UInt64,
484        maxSHRD: UFix64,
485        shrdPerGrade: {String: UFix64},
486        cardsPerPack: UInt64,
487        price: UFix64,
488        startTime: UFix64,
489        endTime: UFix64,
490        maxPerWallet: UInt64,
491        cardTypeWeights: [[UInt32]],
492        cardTypes: [[UInt64]],
493        pausableCap: Capability<&{Pausable.PausableExternal}>
494        ): @Sales {
495        pre {
496            adminRef != nil : "adminRef is nil"
497        }
498        return <- create Sales(
499            name: name,
500            totalSupply: totalSupply,
501            maxSHRD: maxSHRD,
502            shrdPerGrade: shrdPerGrade, 
503            cardsPerPack: cardsPerPack, 
504            price: price, 
505            startTime: startTime,
506            endTime: endTime,
507            maxPerWallet: maxPerWallet,
508            cardTypeWeights: cardTypeWeights,
509            cardTypes: cardTypes,
510            pausableCap: pausableCap
511        ) 
512    }
513
514    pub fun createSalesCollection(adminRef: &MotoGPAdmin.Admin): @SalesCollection { 
515        pre {
516            adminRef != nil : "adminRef is nil"
517        }
518        return <- create SalesCollection()
519    }
520
521    pub fun setCardMintProxyCapability(adminRef: &MotoGPAdmin.Admin, capability: Capability<&CardMintAccess.MintProxy{CardMintAccess.MintProxyPrivate}>) {
522         pre {
523            adminRef != nil : "adminRef is nil"
524            capability.check() == true : "capability.check() is false"
525            capability!.borrow() != nil : "can't borrow capability"
526        }
527        self.cardMintProxyCap = capability
528    }
529
530    pub fun setSHRDMintProxyCapability(adminRef: &MotoGPAdmin.Admin, capability: Capability<&SHRDMintAccess.MintProxy{SHRDMintAccess.MintProxyPrivate}>) {
531         pre {
532            adminRef != nil : "adminRef is nil"
533            capability.check() == true : "capability.check() is false"
534            capability!.borrow() != nil : "can't borrow capability"
535        }
536        self.shrdMintProxyCap = capability
537    }
538
539    pub fun setPaymentReceiverCapability(adminRef: &MotoGPAdmin.Admin, capability: Capability<&{FungibleToken.Receiver}>) {
540        pre {
541            adminRef != nil : "adminRef is nil"
542        }
543        self.paymentReceiverCap = capability
544    }
545
546    access(contract) fun isEqual2DArrayLengths(_ array1: [[AnyStruct]], _ array2: [[AnyStruct]]): Bool {
547        if (array1.length != array2.length) {
548            return false
549        }
550
551        for i, innerArray1 in array1 {
552            if (innerArray1.length != array2[i].length) {
553                return false
554            }
555        }
556
557        return true
558    }
559
560    access(contract) fun isValidSignature(
561        publicKey: String, 
562        signatureAlgorithm: SignatureAlgorithm,
563        hashAlgorithm: HashAlgorithm,
564        signature: String, 
565        message: String): Bool {        
566            let pk = PublicKey(
567                publicKey: publicKey.decodeHex(),
568                signatureAlgorithm: signatureAlgorithm
569            )
570
571            let isValid = pk.verify(
572                signature: signature.decodeHex(),
573                signedData: message.utf8,
574                domainSeparationTag: "FLOW-V0.0-user",
575                hashAlgorithm: hashAlgorithm
576            )
577            return isValid
578    }
579
580    access(contract) fun mintSHRD(amount: UFix64): @SHRD.Vault {
581        return <- self.shrdMintProxyCap!.borrow()!.mint(amount: amount)
582    }
583
584    access(contract) fun mintCards(cardIDs: [UInt64], serials: [UInt64]): @MotoGPCard.Collection {
585        pre {
586            cardIDs.length == serials.length : "Inconsistent array lengths"
587        }
588
589        let collection <- MotoGPCard.createEmptyCollection()
590        for index, cardID in cardIDs {
591            let card <- self.cardMintProxyCap!.borrow()!.mint(cardID: cardID, serial: serials[index]) 
592            collection.deposit(token: <- card)
593        }
594        return <- collection
595    }
596
597    pub let SalesCollectionStoragePath: StoragePath
598    pub let SalesCollectionPublicPath: PublicPath
599    pub let SalesCollectionPrivatePath: PrivatePath
600    pub let pathIdentifier:String
601    pub let orderIds: {String:UInt64} 
602    access(contract) var shrdMintProxyCap: Capability<&SHRDMintAccess.MintProxy{SHRDMintAccess.MintProxyPrivate}>?
603    access(contract) var cardMintProxyCap: Capability<&CardMintAccess.MintProxy{CardMintAccess.MintProxyPrivate}>?
604    access(contract) var paymentReceiverCap: Capability<&{FungibleToken.Receiver}>?
605    
606
607    init() {
608        self.orderIds = {}
609        self.shrdMintProxyCap = nil
610        self.cardMintProxyCap = nil
611        self.paymentReceiverCap = nil
612        self.SalesCollectionStoragePath = /storage/salesCappedCollection
613        self.SalesCollectionPublicPath = /public/salesCappedCollection
614        self.SalesCollectionPrivatePath = /private/salesCappedCollection
615        self.pathIdentifier = "SalesCapped"
616    }
617
618}