Smart Contract
TheFabricantS2Minting
A.7752ea736384322f.TheFabricantS2Minting
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