Smart Contract
AnalogMinter
A.427ceada271aa0b1.AnalogMinter
1import Analogs from 0x427ceada271aa0b1
2import FungibleToken from 0xf233dcee88fe0abe
3import NonFungibleToken from 0x1d7e57aa55817448
4import FlowUtilityToken from 0xead892083b3e2c6c
5
6pub contract AnalogMinter {
7
8 pub event ContractInitialized()
9
10 pub let AdminStoragePath: StoragePath
11
12 pub var mintableSets: {UInt64: MintableSet}
13
14 pub struct MintableSet {
15 pub var privateSalePrice: UFix64
16 pub var publicSalePrice: UFix64
17 pub var whitelistedAccounts: {Address: UInt64}
18 access(self) var metadata: {String: String}
19
20 access(account) fun addWhiteListAddress(address: Address, amount: UInt64) {
21 pre {
22 self.whitelistedAccounts[address] == nil: "Provided Address is already whitelisted"
23 }
24 self.whitelistedAccounts[address] = amount
25 }
26
27 access(account) fun removeWhiteListAddress(address: Address) {
28 pre {
29 self.whitelistedAccounts[address] != nil: "Provided Address is not whitelisted"
30 }
31 self.whitelistedAccounts.remove(key: address)
32 }
33
34 access(account) fun pruneWhitelist() {
35 self.whitelistedAccounts = {}
36 }
37
38 access(account) fun updateWhiteListAddressAmount(address: Address, amount: UInt64) {
39 pre {
40 self.whitelistedAccounts[address] != nil:
41 "Provided Address is not whitelisted"
42 }
43 self.whitelistedAccounts[address] = amount
44 }
45
46 access(account) fun updatePrivateSalePrice(price: UFix64) {
47 self.privateSalePrice = price
48 }
49
50 access(account) fun updatePublicSalePrice(price: UFix64) {
51 self.publicSalePrice = price
52 }
53
54 init(privateSalePrice: UFix64, publicSalePrice: UFix64){
55 self.privateSalePrice = privateSalePrice
56 self.publicSalePrice= publicSalePrice
57 self.whitelistedAccounts = {}
58 self.metadata = {}
59 }
60 }
61
62 pub fun mintPrivateNFTWithFUT(buyer: Address, setID: UInt64, paymentVault: @FungibleToken.Vault, merchantAccount: Address, numberOfTokens: UInt64) {
63 pre {
64 AnalogMinter.mintableSets[setID]!.whitelistedAccounts[buyer]! >= 1:
65 "Requesting account is not whitelisted"
66 // This code is commented for future use case. In case addresses needs a buy amount limit for private sales, this should be discommented.
67 // AnalogMinter.mintableSets[setID]!.whitelistedAccounts[buyer]! >= numberOfTokens: "purchaseAmount exceeds allowed whitelist spots"
68 paymentVault.balance >= UFix64(numberOfTokens) * AnalogMinter.mintableSets[setID]!.privateSalePrice:
69 "Insufficient payment amount."
70 paymentVault.getType() == Type<@FlowUtilityToken.Vault>():
71 "payment type not FlowUtilityToken.Vault."
72 }
73
74 let admin = self.account.borrow<&Analogs.Admin>(from: Analogs.AdminStoragePath)
75 ?? panic("Could not borrow a reference to the Analogs Admin")
76
77 let set = admin.borrowSet(setID: setID)
78 // Check set availability
79 if (set.getAvailableTemplateIDs().length == 0) { panic("Set is empty") }
80 // Check set eligibility
81 if (set.locked) { panic("Set is locked") }
82 if (set.isPublic) { panic("Cannot mint public set with mintPrivateNFTWithFUT") }
83
84 // Get FUT receiver reference of Analogs merchant account
85 let merchantFUTReceiverRef = getAccount(set.analogRoyaltyAddress).getCapability<&{FungibleToken.Receiver}>(/public/flowUtilityTokenReceiver)
86 assert(merchantFUTReceiverRef.borrow() != nil, message: "Missing or mis-typed merchant FUT receiver")
87 // Deposit FUT to Analogs merchant account FUT Vault (it's then forwarded to the main FUT contract afterwards)
88 merchantFUTReceiverRef.borrow()!.deposit(from: <-paymentVault)
89
90 // Get buyer collection public to receive Analogs
91 let recipient = getAccount(buyer)
92 let NFTReceiver = recipient.getCapability(Analogs.CollectionPublicPath)
93 .borrow<&{NonFungibleToken.CollectionPublic}>()
94 ?? panic("Could not get receiver reference to the NFT Collection")
95
96 // Mint Analogs NFTs per purchaseAmount
97 var mintCounter = numberOfTokens
98 while(mintCounter > 0) {
99 admin.mintNFT(recipient: NFTReceiver, setID: setID)
100 mintCounter = mintCounter - 1
101 }
102
103 // Empty whitelist spot
104 if (AnalogMinter.mintableSets[setID]!.whitelistedAccounts[buyer]! - numberOfTokens == 0) {
105 AnalogMinter.mintableSets[setID]!.removeWhiteListAddress(address: buyer)
106 } else {
107 let newAmount = AnalogMinter.mintableSets[setID]!.whitelistedAccounts[buyer]! - numberOfTokens
108 AnalogMinter.mintableSets[setID]!.updateWhiteListAddressAmount(address: buyer, amount: newAmount)
109 }
110 }
111
112 pub fun mintPublicNFTWithFUT(buyer: Address, setID: UInt64, paymentVault: @FungibleToken.Vault, merchantAccount: Address, numberOfTokens: UInt64) {
113 pre {
114 numberOfTokens <= 4:
115 "purchaseAmount too large"
116 paymentVault.balance >= UFix64(numberOfTokens) * AnalogMinter.mintableSets[setID]!.publicSalePrice:
117 "Insufficient payment amount."
118 paymentVault.getType() == Type<@FlowUtilityToken.Vault>():
119 "payment type not FlowUtilityToken.Vault."
120 }
121
122 let admin = self.account.borrow<&Analogs.Admin>(from: Analogs.AdminStoragePath)
123 ?? panic("Could not borrow a reference to the Analogs Admin")
124
125 let set = admin.borrowSet(setID: setID)
126 // Check set availability
127 if (set.getAvailableTemplateIDs().length == 0) { panic("Set is empty") }
128 // Check set eligibility
129 if (set.locked) { panic("Set is locked") }
130 if (!set.isPublic) { panic("Cannot mint private set with mintPublicNFTWithFUT") }
131
132 // Get FUT receiver reference of Analogs merchant account
133 let merchantFUTReceiverRef = getAccount(set.analogRoyaltyAddress).getCapability<&{FungibleToken.Receiver}>(/public/flowUtilityTokenReceiver)
134 assert(merchantFUTReceiverRef.borrow() != nil, message: "Missing or mis-typed merchant FUT receiver")
135 // Deposit FUT to Analogs merchant account FUT Vault (it's then forwarded to the main FUT contract afterwards)
136 merchantFUTReceiverRef.borrow()!.deposit(from: <-paymentVault)
137
138 // Get buyer collection public to receive Analogs
139 let recipient = getAccount(buyer)
140 let NFTReceiver = recipient.getCapability(Analogs.CollectionPublicPath)
141 .borrow<&{NonFungibleToken.CollectionPublic}>()
142 ?? panic("Could not get receiver reference to the NFT Collection")
143
144 // Mint Analogs NFTs per purchaseAmount
145 var mintCounter = numberOfTokens
146 while(mintCounter > 0) {
147 admin.mintNFT(recipient: NFTReceiver, setID: setID)
148 mintCounter = mintCounter - 1
149 }
150 }
151
152 pub fun mintFreeNFT(buyer: Address, numberOfTokens: UInt64) {
153 pre {
154 numberOfTokens > 0: "numberOfTokens must be greater than 0"
155 }
156 let setID: UInt64 = 1
157 var mintedCount: UInt64? = AnalogMinter.mintableSets[setID]!.whitelistedAccounts[buyer]
158 if mintedCount == nil {
159 mintedCount = 0
160 AnalogMinter.mintableSets[setID]!.addWhiteListAddress(address: buyer, amount: numberOfTokens)
161 } else {
162 AnalogMinter.mintableSets[setID]!.updateWhiteListAddressAmount(address: buyer, amount: mintedCount! + numberOfTokens)
163 }
164 assert(mintedCount! + numberOfTokens <= 5, message: "You exceed the minting limit")
165
166 let admin = self.account.borrow<&Analogs.Admin>(from: Analogs.AdminStoragePath)
167 ?? panic("Could not borrow a reference to the Analogs Admin")
168
169 let set = admin.borrowSet(setID: setID)
170 if (set.getAvailableTemplateIDs().length == 0) {
171 panic("Set is empty")
172 }
173 if (set.locked) {
174 panic("Set is locked")
175 }
176
177 let buyerFUTReceiverRef = getAccount(buyer).getCapability<&{FungibleToken.Receiver}>(/public/flowUtilityTokenReceiver)
178 assert(buyerFUTReceiverRef.borrow() != nil, message: "Missing or mis-typed buyer FUT receiver")
179
180 let recipient = getAccount(buyer)
181 let NFTReceiver = recipient.getCapability(Analogs.CollectionPublicPath)
182 .borrow<&{NonFungibleToken.CollectionPublic}>()
183 ?? panic("Could not get receiver reference to the NFT Collection")
184
185 var mintCounter = numberOfTokens
186 while(mintCounter > 0) {
187 admin.mintNFT(recipient: NFTReceiver, setID: setID)
188 mintCounter = mintCounter - 1
189 }
190 }
191
192 pub resource Admin {
193
194 pub fun createMintableSet(setID: UInt64, privateSalePrice: UFix64, publicSalePrice: UFix64) {
195 pre {
196 AnalogMinter.mintableSets[setID] == nil: "Set already exists"
197 }
198 AnalogMinter.mintableSets[setID] = MintableSet(privateSalePrice: privateSalePrice, publicSalePrice: publicSalePrice)
199 }
200
201 pub fun addWhiteListAddress(setID: UInt64, address: Address, amount: UInt64) {
202 AnalogMinter.mintableSets[setID]!.addWhiteListAddress(address: address, amount: amount)
203 }
204
205 pub fun removeWhiteListAddress(setID: UInt64, address: Address) {
206 AnalogMinter.mintableSets[setID]!.removeWhiteListAddress(address: address)
207 }
208
209 pub fun pruneWhitelist(setID: UInt64) {
210 AnalogMinter.mintableSets[setID]!.pruneWhitelist()
211 }
212
213 pub fun updateWhiteListAddressAmount(setID: UInt64, address: Address, amount: UInt64) {
214 AnalogMinter.mintableSets[setID]!.updateWhiteListAddressAmount(address: address, amount: amount)
215 }
216
217 pub fun updatePrivateSalePrice(setID: UInt64, price: UFix64) {
218 AnalogMinter.mintableSets[setID]!.updatePrivateSalePrice(price: price)
219 }
220
221 pub fun updatePublicSalePrice(setID: UInt64, price: UFix64) {
222 AnalogMinter.mintableSets[setID]!.updatePublicSalePrice(price: price)
223 }
224 }
225
226 pub fun getWhitelistedAccounts(setID: UInt64): {Address: UInt64} {
227 return AnalogMinter.mintableSets[setID]!.whitelistedAccounts
228 }
229
230 init() {
231 self.AdminStoragePath = /storage/AnalogMinterAdmin
232
233 self.mintableSets = {}
234
235 let admin <- create Admin()
236 self.account.save(<-admin, to: self.AdminStoragePath)
237
238 emit ContractInitialized()
239 }
240}