Smart Contract

CollectorMinter

A.7c8995e83c4b1843.CollectorMinter

Deployed

2h ago
Feb 28, 2026, 09:41:11 PM UTC

Dependents

0 imports
1// SPDX-License-Identifier: UNLICENSED
2
3import FungibleToken from 0xf233dcee88fe0abe
4import NonFungibleToken from 0x1d7e57aa55817448
5import DapperUtilityCoin from 0xead892083b3e2c6c
6import Collector from 0x7c8995e83c4b1843
7
8pub contract CollectorMinter {
9  pub event ContractInitialized(merchantAccount: Address)
10
11  pub let AdminStoragePath: StoragePath
12  
13  pub var privateSaleMaxTokens: UInt64
14  pub var privateSalePrice: UFix64
15  pub var presaleMaxTokens: UInt64
16  pub var presalePrice: UFix64
17  pub var publicSaleMaxTokens: UInt64
18  pub var publicSalePrice: UFix64
19
20  pub var privateSaleRegistrationOpen: Bool
21  pub var presaleRegistrationOpen: Bool
22
23  access(self) var privateSaleAccounts: {Address: UInt64}
24  access(self) var presaleAccounts: {Address: UInt64}
25  access(self) var publicSaleAccounts: {Address: UInt64}
26
27  pub var merchantAccount: Address
28
29  pub fun registerForPrivateSale(buyer: Address) {
30    pre {
31      self.privateSaleRegistrationOpen == true:
32        "Private sale registration is closed"
33      self.privateSaleAccounts[buyer] == nil:
34        "Address already registered for the private sale"
35    }
36
37    self.privateSaleAccounts[buyer] = self.privateSaleMaxTokens
38  }
39
40  pub fun privateSaleMintNFTWithDUC(buyer: Address, setID: UInt64, paymentVault: @FungibleToken.Vault, numberOfTokens: UInt64, merchantAccount: Address) {
41    pre {
42      self.privateSaleAccounts[buyer]! >= 0:
43        "Requesting account is not whitelisted"
44      numberOfTokens <= self.privateSaleMaxTokens:
45        "Purchase amount exceeds maximum allowed"
46      self.privateSaleAccounts[buyer]! >= numberOfTokens:
47        "Purchase amount exceeds maximum buyer allowance"
48      paymentVault.balance >= UFix64(numberOfTokens) * self.privateSalePrice:
49        "Insufficient payment amount"
50      paymentVault.getType() == Type<@DapperUtilityCoin.Vault>():
51        "Payment type not DapperUtilityCoin"
52      self.merchantAccount == merchantAccount:
53        "Mismatching merchant account"
54    }
55
56    let admin = self.account.borrow<&Collector.Admin>(from: Collector.AdminStoragePath)
57      ?? panic("Could not borrow a reference to the collector admin")
58
59    let set = admin.borrowSet(id: setID)
60
61    // Check set availability
62    if (set.getAvailableTemplateIDs().length == 0) { panic("Set is empty") }
63
64    // Check set eligibility
65    if (set.isPublic) { panic("Cannot mint public set with privateSaleMintNFTWithDUC") }
66
67    // Get DUC receiver reference of Collector merchant account
68    let merchantDUCReceiverRef = getAccount(self.merchantAccount).getCapability<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)
69    assert(merchantDUCReceiverRef.borrow() != nil, message: "Missing or mis-typed merchant DUC receiver")
70
71    // Deposit DUC to Collector merchant account DUC Vault (it's then forwarded to the main DUC contract afterwards)
72    merchantDUCReceiverRef.borrow()!.deposit(from: <-paymentVault)
73
74    // Get buyer collection public to receive Collector
75    let recipient = getAccount(buyer)
76    let NFTReceiver = recipient.getCapability(Collector.CollectionPublicPath)
77      .borrow<&{NonFungibleToken.CollectionPublic}>()
78      ?? panic("Could not get receiver reference to the NFT collection")
79
80    // Mint Collector NFTs
81    var mintCounter = numberOfTokens
82    while(mintCounter > 0) {
83      admin.mintNFT(recipient: NFTReceiver, setID: setID)
84      mintCounter = mintCounter - 1
85    }
86
87    // Remove utilized spots
88    self.privateSaleAccounts[buyer] = self.privateSaleAccounts[buyer]! - numberOfTokens
89  }
90
91  pub fun registerForPresale(buyer: Address) {
92    pre {
93      self.presaleRegistrationOpen == true:
94        "Presale registration is closed"
95      self.presaleAccounts[buyer] == nil:
96        "Address already registered for the presale"
97    }
98
99    self.presaleAccounts[buyer] = self.presaleMaxTokens
100  }
101
102  pub fun presaleMintNFTWithDUC(buyer: Address, setID: UInt64, paymentVault: @FungibleToken.Vault, numberOfTokens: UInt64, merchantAccount: Address) {
103    pre {
104      self.presaleAccounts[buyer]! >= 0:
105        "Requesting account is not whitelisted"
106      numberOfTokens <= self.presaleMaxTokens:
107        "Purchase amount exceeds maximum allowed"
108      self.presaleAccounts[buyer]! >= numberOfTokens:
109        "Purchase amount exceeds maximum buyer allowance"
110      paymentVault.balance >= UFix64(numberOfTokens) * self.presalePrice:
111        "Insufficient payment amount"
112      paymentVault.getType() == Type<@DapperUtilityCoin.Vault>():
113        "Payment type not DapperUtilityCoin"
114      self.merchantAccount == merchantAccount:
115        "Mismatching merchant account"
116      Collector.totalSupply < 3506:
117        "Reached max capacity"
118    }
119
120    let admin = self.account.borrow<&Collector.Admin>(from: Collector.AdminStoragePath)
121      ?? panic("Could not borrow a reference to the collector admin")
122
123    let set = admin.borrowSet(id: setID)
124
125    // Check set availability
126    if (set.getAvailableTemplateIDs().length == 0) { panic("Set is empty") }
127
128    // Check set eligibility
129    if (set.isPublic) { panic("Cannot mint public set with presaleMintNFTWithDUC") }
130
131    // Get DUC receiver reference of Collector merchant account
132    let merchantDUCReceiverRef = getAccount(self.merchantAccount).getCapability<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)
133    assert(merchantDUCReceiverRef.borrow() != nil, message: "Missing or mis-typed merchant DUC receiver")
134
135    // Deposit DUC to Collector merchant account DUC Vault (it's then forwarded to the main DUC contract afterwards)
136    merchantDUCReceiverRef.borrow()!.deposit(from: <-paymentVault)
137
138    // Get buyer collection public to receive Collector
139    let recipient = getAccount(buyer)
140    let NFTReceiver = recipient.getCapability(Collector.CollectionPublicPath)
141      .borrow<&{NonFungibleToken.CollectionPublic}>()
142      ?? panic("Could not get receiver reference to the NFT collection")
143
144    // Mint Collector NFTs
145    var mintCounter = numberOfTokens
146    while(mintCounter > 0) {
147      admin.mintNFT(recipient: NFTReceiver, setID: setID)
148      mintCounter = mintCounter - 1
149    }
150
151    // Remove utilized spots
152    self.presaleAccounts[buyer] = self.presaleAccounts[buyer]! - numberOfTokens
153  }
154
155  pub fun publicSaleMintNFTWithDUC(buyer: Address, setID: UInt64, paymentVault: @FungibleToken.Vault, numberOfTokens: UInt64, merchantAccount: Address) {
156    pre {
157      numberOfTokens <= self.publicSaleMaxTokens:
158        "Purchase amount exeeds maximum allowed"
159      paymentVault.balance >= UFix64(numberOfTokens) * self.publicSalePrice:
160        "Insufficient payment amount"
161      paymentVault.getType() == Type<@DapperUtilityCoin.Vault>():
162        "Payment type not DapperUtilityCoin"
163      self.merchantAccount == merchantAccount:
164        "Mismatching merchant account"
165      Collector.totalSupply < 3506:
166        "Reached max capacity"
167    }
168
169    // Add address to public sale accounts list
170    if (self.publicSaleAccounts[buyer] == nil) {
171      self.publicSaleAccounts[buyer] = self.publicSaleMaxTokens
172    }
173
174    // Check buyer hasn't exceeded their allowance
175    if (self.publicSaleAccounts[buyer]! < numberOfTokens) {
176      panic("Purchase amount exceeds maximum buyer allowance")
177    }
178
179    let admin = self.account.borrow<&Collector.Admin>(from: Collector.AdminStoragePath)
180      ?? panic("Could not borrow a reference to the collector admin")
181
182    let set = admin.borrowSet(id: setID)
183
184    // Check set availability
185    if (set.getAvailableTemplateIDs().length == 0) { panic("Set is empty") }
186
187    // Check set eligibility
188    if (!set.isPublic) { panic("Cannot mint private set with publicSaleMintNFTWithDUC") }
189
190    // Get DUC receiver reference of Collector merchant account
191    let merchantDUCReceiverRef = getAccount(self.merchantAccount).getCapability<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)
192    assert(merchantDUCReceiverRef.borrow() != nil, message: "Missing or mis-typed merchant DUC receiver")
193
194    // Deposit DUC to Collector merchant account DUC Vault (it's then forwarded to the main DUC contract afterwards)
195    merchantDUCReceiverRef.borrow()!.deposit(from: <-paymentVault)
196
197    // Get buyer collection public to receive Collector
198    let recipient = getAccount(buyer)
199    let NFTReceiver = recipient.getCapability(Collector.CollectionPublicPath)
200      .borrow<&{NonFungibleToken.CollectionPublic}>()
201      ?? panic("Could not get receiver reference to the NFT Collection")
202
203    // Mint Collector NFTs per purchaseAmount
204    var mintCounter = numberOfTokens
205    while(mintCounter > 0) {
206      admin.mintNFT(recipient: NFTReceiver, setID: setID)
207      mintCounter = mintCounter - 1
208    }
209
210    // Remove utilized spots
211    self.publicSaleAccounts[buyer] = self.publicSaleAccounts[buyer]! - numberOfTokens
212  }
213
214  pub resource Admin {
215    //
216    // PRIVATE SALE FUNCTIONS
217    //
218    pub fun addAccountToPrivateSale(address: Address, amount: UInt64) {
219      pre {
220        amount <= CollectorMinter.privateSaleMaxTokens:
221          "Unable to allocate more private sale spots"
222        CollectorMinter.privateSaleAccounts[address] == nil:
223          "Provided address already added to the private sale"
224      }
225      CollectorMinter.privateSaleAccounts[address] = amount
226    }
227
228    pub fun removeAccountFromPrivateSale(address: Address) {
229      pre {
230        CollectorMinter.privateSaleAccounts[address] != nil:
231          "Provided address is not in the private sale list"
232      }
233      CollectorMinter.privateSaleAccounts.remove(key: address)
234    }
235
236    pub fun updatePrivateSaleAccountAmount(address: Address, amount: UInt64) {
237      pre {
238        CollectorMinter.privateSaleAccounts[address] != nil:
239          "Provided address is not in the private sale list"
240      }
241      CollectorMinter.privateSaleAccounts[address] = amount
242    }
243
244    pub fun updatePrivateSaleMaxTokens(amount: UInt64) {
245      CollectorMinter.privateSaleMaxTokens = amount
246    }
247
248    pub fun updatePrivateSalePrice(price: UFix64) {
249      CollectorMinter.privateSalePrice = price
250    }
251
252    pub fun prunePrivateSaleAccounts() {
253      CollectorMinter.privateSaleAccounts = {}
254    }
255
256    pub fun closePrivateSaleRegistration() {
257      CollectorMinter.privateSaleRegistrationOpen = false
258    }
259
260    pub fun openPrivateSaleRegistration() {
261      CollectorMinter.privateSaleRegistrationOpen = true
262    }
263
264    //
265    // PRESALE FUNCTIONS
266    //
267    pub fun addAccountToPresale(address: Address, amount: UInt64) {
268      pre {
269        amount <= CollectorMinter.presaleMaxTokens:
270          "Unable to allocate more presale spots"
271        CollectorMinter.presaleAccounts[address] == nil:
272          "Provided address already added to the presale"
273      }
274      CollectorMinter.presaleAccounts[address] = amount
275    }
276
277    pub fun removeAccountFromPresale(address: Address) {
278      pre {
279        CollectorMinter.presaleAccounts[address] != nil:
280          "Provided address is not in the presale list"
281      }
282      CollectorMinter.presaleAccounts.remove(key: address)
283    }
284
285    pub fun updatePresaleAccountAmount(address: Address, amount: UInt64) {
286      pre {
287        CollectorMinter.presaleAccounts[address] != nil:
288          "Provided address is not in the presale list"
289      }
290      CollectorMinter.presaleAccounts[address] = amount
291    }
292
293    pub fun updatePresaleMaxTokens(amount: UInt64) {
294      CollectorMinter.presaleMaxTokens = amount
295    }
296
297    pub fun updatePresalePrice(price: UFix64) {
298      CollectorMinter.presalePrice = price
299    }
300
301    pub fun prunePresaleAccounts() {
302      CollectorMinter.presaleAccounts = {}
303    }
304
305    pub fun closePresaleRegistration() {
306      CollectorMinter.presaleRegistrationOpen = false
307    }
308
309    pub fun openPresaleRegistration() {
310      CollectorMinter.presaleRegistrationOpen = true
311    }
312
313    //
314    // PUBLIC SALE FUNCTIONS
315    //
316    pub fun updatePublicSaleMaxTokens(amount: UInt64) {
317      CollectorMinter.publicSaleMaxTokens = amount
318    }
319
320    pub fun updatePublicSalePrice(price: UFix64) {
321      CollectorMinter.publicSalePrice = price
322    }
323
324    pub fun prunePublicSaleAccounts() {
325      CollectorMinter.publicSaleAccounts = {}
326    }
327
328    //
329    // COMMON
330    //
331    pub fun updateMerchantAccount(newAddr: Address) {
332      CollectorMinter.merchantAccount = newAddr
333    }
334  }
335
336  pub fun getPrivateSaleAccounts(): {Address: UInt64} {
337    return self.privateSaleAccounts
338  }
339  
340  pub fun getPresaleAccounts(): {Address: UInt64} {
341    return self.presaleAccounts
342  }
343
344  pub fun getPublicSaleAccounts(): {Address: UInt64} {
345    return self.publicSaleAccounts
346  }
347
348  init(merchantAccount: Address) {
349    self.AdminStoragePath = /storage/CollectorMinterAdmin
350
351    self.privateSaleRegistrationOpen = true
352    self.presaleRegistrationOpen = true
353
354    self.privateSaleMaxTokens = 1
355    self.privateSalePrice = 10.00
356    self.privateSaleAccounts = {}
357
358    self.presaleMaxTokens = 3
359    self.presalePrice = 129.00
360    self.presaleAccounts = {}
361
362    self.publicSaleMaxTokens = 3
363    self.publicSalePrice = 179.00
364    self.publicSaleAccounts = {}
365
366    // For testnet this should be 0x03df89ac89a3d64a
367    // For mainnet this should be 0xfe15c4f52a58c75e
368    self.merchantAccount = merchantAccount
369
370    let admin <- create Admin()
371    self.account.save(<-admin, to: self.AdminStoragePath)
372
373    emit ContractInitialized(merchantAccount: merchantAccount)
374  }
375}
376