Smart Contract

TheFabricantS2Minting

A.7752ea736384322f.TheFabricantS2Minting

Deployed

3h ago
Feb 28, 2026, 08:14:46 PM UTC

Dependents

0 imports
1/*
2    Description: TheFabricantS2Minting Contract
3   
4    This contract lets users mint TheFabricantS2ItemNFT NFTs for a specified amount of FLOW
5*/
6
7// NOTE: WHEN PUSHING TO MN OR TN FOR WOW, YOU MUST SET THE EDITION CORRECTLY!!!!!!
8// For Testnet: 161
9// MN 486
10// for testing: 1
11
12import NonFungibleToken from 0x1d7e57aa55817448
13import FungibleToken from 0xf233dcee88fe0abe
14import TheFabricantS2GarmentNFT from 0x7752ea736384322f
15import TheFabricantS2MaterialNFT from 0x7752ea736384322f
16import TheFabricantS2ItemNFT from 0x7752ea736384322f
17import ItemNFT from 0xfc91de5e6566cc7c
18import TheFabricantS1ItemNFT from 0x09e03b1f871b3513
19import TheFabricantMysteryBox_FF1 from 0xa0cbe021821c0965
20import FlowToken from 0x1654653399040a61
21import TheFabricantAccessPass from 0x7752ea736384322f
22pub contract TheFabricantS2Minting{
23
24    pub event ItemMintedAndTransferred(
25        recipientAddr: Address, 
26        garmentDataID: UInt32, 
27        materialDataID: UInt32, 
28        primaryColor: String, 
29        secondaryColor: String, 
30        itemID: UInt64, 
31        itemDataID: UInt32, 
32        name: String, 
33        eventName: String, 
34        edition: String, 
35        variant: String, 
36        season: String
37    )
38
39    pub event EventAdded(eventName: String, eventDetail: EventDetail)
40
41    pub event IsEventClosedChanged(eventName: String, isClosed: Bool)
42    pub event MaxMintAmountChanged(eventName: String, newMax: UInt32)
43    pub event PaymentTypeChanged(eventName: String, newPaymentType: Type)
44    pub event PaymentAmountChanged(eventName: String, newPaymentAmount: UFix64)
45
46    pub event ItemMinterCapabilityChanged(address: Address)
47    pub event GarmentMinterCapabilityChanged(address: Address)
48    pub event MaterialMinterCapabilityChanged(address: Address)
49    pub event PaymentReceiverCapabilityChanged(address: Address, paymentType: Type)
50
51    pub let AdminStoragePath: StoragePath
52    pub let MinterStoragePath: StoragePath
53
54    access(self) var eventsDetail: {String: EventDetail}
55
56    access(contract) var itemMinterCapability: Capability<&TheFabricantS2ItemNFT.Admin>?
57    access(contract) var garmentMinterCapability: Capability<&TheFabricantS2GarmentNFT.Admin>?
58    access(contract) var materialMinterCapability: Capability<&TheFabricantS2MaterialNFT.Admin>?
59
60    access(contract) var paymentReceiverCapability: Capability<&{FungibleToken.Receiver}>?
61
62
63    pub struct EventDetail {
64
65        access(self) var addressMintCount: {Address: UInt32}
66
67        pub var closed: Bool
68
69        pub var paymentAmount: UFix64
70
71        pub var paymentType: Type
72
73        pub var maxMintAmount: UInt32
74
75        init(paymentAmount: UFix64, paymentType: Type, maxMintAmount: UInt32) {
76            self.addressMintCount = {}
77            self.closed = true
78            self.paymentAmount = paymentAmount
79            self.paymentType = paymentType
80            self.maxMintAmount = maxMintAmount
81        }
82
83        pub fun changeIsEventClosed(isClosed: Bool) {
84            self.closed = isClosed
85        }
86
87        pub fun changeMaxMintAmount(newMax: UInt32) {
88            self.maxMintAmount = newMax
89        }
90
91        pub fun changePaymentType(newPaymentType: Type) {
92            self.paymentType = newPaymentType
93        }
94
95        pub fun changePaymentAmount(newPaymentAmount: UFix64) {
96            self.paymentAmount = newPaymentAmount
97        }
98
99        access(contract) fun incrementAddressMintCount(address: Address) {
100            if(self.addressMintCount[address] == nil) {
101                self.addressMintCount[address] = 1
102            } else {
103                self.addressMintCount[address] = self.addressMintCount[address]! + 1
104            }
105        }
106
107        pub fun getAddressMintCount(): {Address: UInt32} {
108            return self.addressMintCount
109        }
110    }
111
112    // check if an address holds certain nfts to allow mint
113    pub fun doesAddressHoldAccessPass(address: Address): Bool {
114        var hasTheFabricantAccessPass: Bool = false
115        
116        if (getAccount(address).getCapability<&{TheFabricantAccessPass.TheFabricantAccessPassCollectionPublic}>(TheFabricantAccessPass.TheFabricantAccessPassCollectionPublicPath).check()) {
117            let collectionRef = getAccount(address).getCapability(TheFabricantAccessPass.TheFabricantAccessPassCollectionPublicPath)
118                                .borrow<&{TheFabricantAccessPass.TheFabricantAccessPassCollectionPublic}>()!
119            hasTheFabricantAccessPass = collectionRef.getIDs().length > 0
120        }
121
122        return hasTheFabricantAccessPass
123    }
124
125    pub resource Minter{
126        
127        //call S2ItemNFT's mintItem function
128        pub fun mintAndTransferItem(
129            garmentDataID: UInt32,
130            materialDataID: UInt32,
131            primaryColor: String,
132            secondaryColor: String,
133            payment: @FungibleToken.Vault,
134            eventName: String,
135            accessPassRef: &TheFabricantAccessPass.NFT
136            ): @TheFabricantS2ItemNFT.NFT {
137    
138            pre {
139                TheFabricantS2Minting.eventsDetail[eventName] != nil:
140                "event does not exist"
141                TheFabricantS2Minting.eventsDetail[eventName]!.closed == false:
142                "minting is closed"
143                payment.isInstance(TheFabricantS2Minting.eventsDetail[eventName]!.paymentType): 
144                "payment vault is not requested fungible token"
145                TheFabricantS2Minting.doesAddressHoldAccessPass(address: self.owner!.address):
146                "address does not have an accesspass" 
147                (accessPassRef != nil && payment.balance == 0.0) || (accessPassRef != nil && payment.balance == TheFabricantS2Minting.eventsDetail[eventName]!.paymentAmount): 
148                "Payment is free if you use a free mint from your access pass, otherwise you must pay the mint fee and hold an access pass"
149                garmentDataID > 12 && materialDataID > 15:
150                "garmentData and materialData not available for this event"
151                (accessPassRef.owner!.address == 0xdc496a70f3b89c08):
152                "Only the archive account can mint during mint test"
153            }
154
155
156
157            // If the user has provided an accessPassRef and 0.0 FLOW, 
158            // then they wish to mint by spending an access unit 
159            if payment.balance == 0.0 {
160                assert(accessPassRef.accessUnits > 0, message: "You have spent all of your access units!" )
161                assert(accessPassRef.campaignName == eventName, message: "You must use the correct AccessPass to mint" )
162                assert(accessPassRef.owner!.address == self.owner!.address, message: "accessPass does not belong to owner")
163                destroy accessPassRef.spendAccessUnit()
164                // Access unit has now been spent and payment vault balance is 0, so the mint will be free
165            }
166
167            if(TheFabricantS2Minting.eventsDetail[eventName]!.getAddressMintCount()[self.owner!.address] != nil) {
168                if(TheFabricantS2Minting.eventsDetail[eventName]!.getAddressMintCount()[self.owner!.address]! >= TheFabricantS2Minting.eventsDetail[eventName]!.maxMintAmount) {
169                    panic("Address has minted max amount of items already")
170                }
171            }
172
173            // mint the garment and material
174            let garment <- TheFabricantS2Minting.garmentMinterCapability!.borrow()!.mintNFT(garmentDataID: garmentDataID)
175            let material <- TheFabricantS2Minting.materialMinterCapability!.borrow()!.mintNFT(materialDataID: materialDataID)  
176
177            // split the royalty from price to garment and material address
178            let garmentData = garment.garment.garmentDataID
179            let garmentRoyalties = TheFabricantS2GarmentNFT.getGarmentData(id: garmentData).getRoyalty()
180            let materialData = material.material.materialDataID
181            let materialRoyalties = TheFabricantS2MaterialNFT.getMaterialData(id: materialData).getRoyalty()
182
183            let garmentRoyaltyCount = UFix64(garmentRoyalties.keys.length)
184            let materialRoyaltyCount = UFix64(materialRoyalties.keys.length)
185            let paymentAmount = payment.balance
186
187            for key in garmentRoyalties.keys {
188                var paymentSplit: UFix64 = (paymentAmount*0.45)/garmentRoyaltyCount
189                if (key == "The Fabricant") {
190                    paymentSplit = (paymentAmount*0.3)
191                }
192                if (key as! String == "World Of Women") {
193                    paymentSplit = (paymentAmount*0.15)
194                }
195                if let garmentRoyaltyReceiver = garmentRoyalties[key]!.wallet.borrow() {
196                   let garmentRoyaltyPaymentCut <- payment.withdraw(amount: paymentSplit)
197                   garmentRoyaltyReceiver.deposit(from: <- garmentRoyaltyPaymentCut)
198                }
199            }
200
201            for key in materialRoyalties.keys {
202                let paymentSplit = (paymentAmount*0.45)/materialRoyaltyCount
203                if let materialRoyaltyReceiver = materialRoyalties[key]!.wallet.borrow() {
204                   let materialRoyaltyPaymentCut <- payment.withdraw(amount: paymentSplit)
205                   materialRoyaltyReceiver.deposit(from: <- materialRoyaltyPaymentCut)
206                }
207            }
208
209            // testnet: 161
210            // mainnet: 486
211            // for test purposes we use 1, so before this 2 items were minted,
212            // edition for next season at 1
213            let edition = (TheFabricantS2ItemNFT.totalSupply - 486).toString() 
214
215            // create the metadata for the item
216            let metadatas: {String: TheFabricantS2ItemNFT.Metadata} = {}
217            metadatas["itemImage"] = 
218            TheFabricantS2ItemNFT.Metadata(
219                metadataValue: "https://leela.mypinata.cloud/ipfs/QmU5aYSJ7js6KpuJNw7R7pTBvGmJoucX9GWBWfB6rJFrfa",
220                mutable: true)
221            metadatas["itemVideo"] =
222            TheFabricantS2ItemNFT.Metadata(
223                metadataValue: "https://leela.mypinata.cloud/ipfs/QmcQHb28TADJjzTkJgXKekWs5WFbyFsNwEez8Msc9uZ248/WoW_unboxing_LOOP.mp4",
224                mutable: true)
225            metadatas["itemImage2"] =     
226            TheFabricantS2ItemNFT.Metadata(
227                metadataValue: "",
228                mutable: true)
229            metadatas["itemImage3"] =     
230            TheFabricantS2ItemNFT.Metadata(
231                metadataValue: "",
232                mutable: true)
233            metadatas["itemImage4"] =     
234            TheFabricantS2ItemNFT.Metadata(
235                metadataValue: "",
236                mutable: true)
237            metadatas["season"] =     
238            TheFabricantS2ItemNFT.Metadata(
239                metadataValue: "2",
240                mutable: false)
241            metadatas["edition"] =     
242            TheFabricantS2ItemNFT.Metadata(
243                metadataValue: edition, 
244                mutable: false)
245            metadatas["eventName"] =     
246            TheFabricantS2ItemNFT.Metadata(
247                metadataValue: eventName,
248                mutable: false)
249
250            // create the item data with allocation for the item
251            TheFabricantS2Minting.itemMinterCapability!.borrow()!.createItemDataWithAllocation(
252                garmentDataID: garment.garment.garmentDataID, 
253                materialDataID: material.material.materialDataID, 
254                primaryColor: primaryColor, 
255                secondaryColor: secondaryColor,
256                metadatas: metadatas,
257                coCreator: self.owner!.address)
258
259            // create the royalty struct for the item
260            let royalty = TheFabricantS2ItemNFT.Royalty(
261                    wallet: getAccount(self.owner!.address).getCapability<&FlowToken.Vault{FungibleToken.Receiver}>(/public/flowTokenReceiver),
262                    initialCut: 0.3,
263                    cut: 0.1/3.0
264                )
265
266            //set mint count of transacter as 1 if first time, else increment
267            let eventDetails = TheFabricantS2Minting.eventsDetail[eventName]!
268            eventDetails.incrementAddressMintCount(address: self.owner!.address)
269            
270            //update event detail for eventName with new detail
271            TheFabricantS2Minting.eventsDetail[eventName] = eventDetails
272
273            //set initial name of item to "Season 2 WoW Collection #:id"
274            let name = "Season 2 WoW Collection #".concat(edition)
275
276            //user mints the item
277            let item <- TheFabricantS2ItemNFT.mintNFT(
278                name: name,
279                royaltyVault: royalty, 
280                garment: <- garment, 
281                material: <- material,
282                primaryColor: primaryColor,
283                secondaryColor: secondaryColor)
284
285            emit ItemMintedAndTransferred(
286                recipientAddr: self.owner!.address, 
287                garmentDataID: garmentDataID, 
288                materialDataID: materialDataID, 
289                primaryColor: primaryColor, 
290                secondaryColor: secondaryColor, 
291                itemID: item.id, 
292                itemDataID: item.item.itemDataID, 
293                name: name, 
294                eventName: eventName,
295                edition: edition,
296                variant: "PinkPurse",
297                season: "2")
298
299            //The Fabricant receives the remainder of the payment ofter royalty split
300            TheFabricantS2Minting.paymentReceiverCapability!.borrow()!.deposit(from: <-payment)
301
302            return <- item
303        }
304    }
305
306    pub resource Admin{
307
308        pub fun changeIsEventClosed(eventName: String, isClosed: Bool) {
309            pre {
310                TheFabricantS2Minting.eventsDetail[eventName] != nil: 
311                "eventName doesnt exist"
312            }
313            let eventDetail = TheFabricantS2Minting.eventsDetail[eventName]!
314            eventDetail.changeIsEventClosed(isClosed: isClosed)
315            TheFabricantS2Minting.eventsDetail[eventName] = eventDetail
316
317            emit IsEventClosedChanged(eventName: eventName, isClosed: isClosed)
318        }
319
320        pub fun changeMaxMintAmount(eventName: String, newMax: UInt32) {
321            pre {
322                TheFabricantS2Minting.eventsDetail[eventName] != nil: 
323                "eventName doesnt exist"
324            }
325            let eventDetail = TheFabricantS2Minting.eventsDetail[eventName]!
326            eventDetail.changeMaxMintAmount(newMax: newMax)
327            TheFabricantS2Minting.eventsDetail[eventName] = eventDetail
328
329            emit MaxMintAmountChanged(eventName: eventName, newMax: newMax)
330        }
331
332        pub fun changePaymentType(eventName: String, newPaymentType: Type) {
333            pre {
334                TheFabricantS2Minting.eventsDetail[eventName] != nil: 
335                "eventName doesnt exist"
336            }
337            let eventDetail = TheFabricantS2Minting.eventsDetail[eventName]!
338            eventDetail.changePaymentType(newPaymentType: newPaymentType)
339            TheFabricantS2Minting.eventsDetail[eventName] = eventDetail
340
341            emit PaymentTypeChanged(eventName: eventName, newPaymentType: newPaymentType)
342        }
343
344        pub fun changePaymentAmount(eventName: String, newPaymentAmount: UFix64) {
345            pre {
346                TheFabricantS2Minting.eventsDetail[eventName] != nil: 
347                "eventName doesnt exist"
348            }
349            let eventDetail = TheFabricantS2Minting.eventsDetail[eventName]!
350            eventDetail.changePaymentAmount(newPaymentAmount: newPaymentAmount)
351            TheFabricantS2Minting.eventsDetail[eventName] = eventDetail
352
353            emit PaymentAmountChanged(eventName: eventName, newPaymentAmount: newPaymentAmount)
354        }
355
356        pub fun changeItemMinterCapability(minterCapability: Capability<&TheFabricantS2ItemNFT.Admin>) {
357            TheFabricantS2Minting.itemMinterCapability = minterCapability
358
359            emit ItemMinterCapabilityChanged(address: minterCapability.address)
360        }
361
362        pub fun changeGarmentMinterCapability(minterCapability: Capability<&TheFabricantS2GarmentNFT.Admin>) {
363            TheFabricantS2Minting.garmentMinterCapability = minterCapability
364
365            emit GarmentMinterCapabilityChanged(address: minterCapability.address)
366        }
367        
368        pub fun changeMaterialMinterCapability(minterCapability: Capability<&TheFabricantS2MaterialNFT.Admin>) {
369            TheFabricantS2Minting.materialMinterCapability = minterCapability
370
371            emit MaterialMinterCapabilityChanged(address: minterCapability.address)
372        }
373
374        pub fun changePaymentReceiverCapability(paymentReceiverCapability: Capability<&{FungibleToken.Receiver}>) {
375            TheFabricantS2Minting.paymentReceiverCapability = paymentReceiverCapability
376
377            emit PaymentReceiverCapabilityChanged(address: paymentReceiverCapability.address, paymentType: paymentReceiverCapability.getType())
378        }
379
380        pub fun addEvent(eventName: String, eventDetail: EventDetail){
381            pre {
382                TheFabricantS2Minting.eventsDetail[eventName] == nil:
383                "eventName already exists"
384            }
385            TheFabricantS2Minting.eventsDetail[eventName] = eventDetail
386
387            emit EventAdded(eventName: eventName, eventDetail: eventDetail)
388        }
389        
390        pub fun createNewAdmin(): @Admin {
391            return <-create Admin()
392        }
393    }
394
395    pub fun createNewMinter(): @Minter {
396        return <-create Minter()
397    }
398
399    pub fun getEventsDetail(): {String: EventDetail} {
400        return TheFabricantS2Minting.eventsDetail
401    }
402
403    pub fun getPaymentReceiverAddress(): Address {
404        return TheFabricantS2Minting.paymentReceiverCapability!.address
405    }
406
407    pub fun getMinterCapabilityAddress(): Address {
408        return TheFabricantS2Minting.itemMinterCapability!.address
409    }
410    
411    init() {
412        self.paymentReceiverCapability = nil
413        self.eventsDetail = {}
414        self.itemMinterCapability = nil
415        self.garmentMinterCapability = nil
416        self.materialMinterCapability = nil
417        self.AdminStoragePath = /storage/TheFabricantS2MintingAdmin0028
418        self.MinterStoragePath = /storage/TheFabricantS2Minter0028
419        self.account.save<@Admin>(<- create Admin(), to: self.AdminStoragePath)
420    }
421}
422 
423