Smart Contract

AnalogMinter

A.427ceada271aa0b1.AnalogMinter

Deployed

19h ago
Feb 27, 2026, 10:12:42 PM UTC

Dependents

0 imports
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}