Smart Contract

WineMinter

A.fb27085fbb495d1d.WineMinter

Deployed

3d ago
Mar 01, 2026, 01:38:28 AM UTC

Dependents

0 imports
1// SPDX-License-Identifier: UNLICENSED
2
3import FungibleToken from 0xf233dcee88fe0abe
4import NonFungibleToken from 0x1d7e57aa55817448
5import DapperUtilityCoin from 0xead892083b3e2c6c
6import FlowUtilityToken from 0xead892083b3e2c6c
7import Wine from 0xfb27085fbb495d1d
8
9pub contract WineMinter {
10  pub event ContractInitialized()
11
12  pub let AdminStoragePath: StoragePath
13  
14  pub var saleMaxTokens: {String: {UInt64: UInt64}}
15  pub var salePrices: {String: {UInt64: UFix64}}
16
17  access(self) var saleAccounts: {String: {UInt64: {Address: UInt64}}}
18
19  pub var merchantAccounts: {String: Address}
20
21  access(self) fun initSaleAccounts() {
22    if self.saleAccounts["PUBLIC"] == nil {
23      self.saleAccounts["PUBLIC"] = {}
24    }
25  }
26
27  access(self) fun initPublicSaleAccountsForBuyer(templateID: UInt64, buyer: Address) {
28    if self.saleAccounts["PUBLIC"] == nil {
29      self.saleAccounts["PUBLIC"] = {}
30    }
31
32    if self.saleAccounts["PUBLIC"]![templateID] == nil {
33      let publicSaleAccounts = self.saleAccounts["PUBLIC"]!
34      publicSaleAccounts[templateID] = {}
35      self.saleAccounts["PUBLIC"] = publicSaleAccounts
36    }
37    
38    if self.saleAccounts["PUBLIC"]![templateID]![buyer] == nil {
39      let publicSaleAccounts = self.saleAccounts["PUBLIC"]!
40      let templateSaleAccounts = publicSaleAccounts[templateID]!
41      templateSaleAccounts[buyer] = self.saleMaxTokens["PUBLIC"]![templateID]
42      publicSaleAccounts[templateID] = templateSaleAccounts
43      self.saleAccounts["PUBLIC"] = publicSaleAccounts
44    }
45  }
46
47  access(self) fun updateSaleAccountsForBuyer(templateID: UInt64, buyer: Address, newValue: UInt64) {
48    let publicSaleAccounts = self.saleAccounts["PUBLIC"]!
49    let templateSaleAccounts = publicSaleAccounts[templateID]!
50    templateSaleAccounts[buyer] = newValue
51    publicSaleAccounts[templateID] = templateSaleAccounts
52    self.saleAccounts["PUBLIC"] = publicSaleAccounts
53  }
54
55  pub fun mintNFTWithDUC(buyer: Address, setID: UInt64, templateID: UInt64, paymentVault: @FungibleToken.Vault, merchantAccount: Address) {
56    pre {
57      paymentVault.balance >= self.salePrices["DUC-PUBLIC"]![templateID]!:
58        "Insufficient payment amount"
59      paymentVault.getType() == Type<@DapperUtilityCoin.Vault>():
60        "Payment type not DapperUtilityCoin"
61      self.merchantAccounts["DUC"]! == merchantAccount:
62        "Mismatching merchant account"
63    }
64
65    self.initPublicSaleAccountsForBuyer(templateID: templateID, buyer: buyer)
66    
67    // Check buyer hasn't exceeded their allowance
68    if (self.saleAccounts["PUBLIC"]![templateID]![buyer]! < 1) {
69      panic("Purchase amount exceeds maximum buyer allowance")
70    }
71
72    let admin = self.account.borrow<&Wine.Admin>(from: Wine.AdminStoragePath)
73      ?? panic("Could not borrow a reference to the wine admin")
74
75    let set = admin.borrowSet(id: setID)
76
77    // Check set availability
78    if (set.getTemplateIDs().length == 0) { panic("Set is empty") }
79
80    // Check set eligibility
81    if (!set.isPublic) { panic("Cannot mint private set with mintNFTWithDUC") }
82
83    // Get DUC receiver reference of merchant account
84    let merchantDUCReceiverRef = getAccount(self.merchantAccounts["DUC"]!).getCapability<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)
85    assert(merchantDUCReceiverRef.borrow() != nil, message: "Missing or mis-typed merchant DUC receiver")
86
87    // Deposit DUC to merchant account DUC Vault (it's then forwarded to the main DUC contract afterwards)
88    merchantDUCReceiverRef.borrow()!.deposit(from: <-paymentVault)
89
90    // Get buyer collection public to receive Wine NFT
91    let recipient = getAccount(buyer)
92    let NFTReceiver = recipient.getCapability(Wine.CollectionPublicPath)
93      .borrow<&{NonFungibleToken.CollectionPublic}>()
94      ?? panic("Could not get receiver reference to the NFT Collection")
95
96    // Mint Collector NFT
97    admin.mintNFT(recipient: NFTReceiver, setID: setID, templateID: templateID)
98
99    // Remove utilized spots
100    self.updateSaleAccountsForBuyer(
101      templateID: templateID,
102      buyer: buyer,
103      newValue: self.saleAccounts["PUBLIC"]![templateID]![buyer]! - 1
104    )
105  }
106
107  pub fun mintNFTWithFUT(buyer: Address, setID: UInt64, templateID: UInt64, paymentVault: @FungibleToken.Vault, merchantAccount: Address) {
108    pre {
109      paymentVault.balance >= self.salePrices["FUT-PUBLIC"]![templateID]!:
110        "Insufficient payment amount"
111      paymentVault.getType() == Type<@FlowUtilityToken.Vault>():
112        "Payment type not FlowUtilityToken"
113      self.merchantAccounts["FUT"]! == merchantAccount:
114        "Mismatching merchant account"
115    }
116
117    self.initPublicSaleAccountsForBuyer(templateID: templateID, buyer: buyer)
118    
119    // Check buyer hasn't exceeded their allowance
120    if (self.saleAccounts["PUBLIC"]![templateID]![buyer]! < 1) {
121      panic("Purchase amount exceeds maximum buyer allowance")
122    }
123
124    let admin = self.account.borrow<&Wine.Admin>(from: Wine.AdminStoragePath)
125      ?? panic("Could not borrow a reference to the wine admin")
126
127    let set = admin.borrowSet(id: setID)
128
129    // Check set availability
130    if (set.getTemplateIDs().length == 0) { panic("Set is empty") }
131
132    // Check set eligibility
133    if (!set.isPublic) { panic("Cannot mint private set with mintNFTWithFUT") }
134
135    // Get FUT receiver reference of merchant account
136    let merchantFUTReceiverRef = getAccount(self.merchantAccounts["FUT"]!).getCapability<&{FungibleToken.Receiver}>(/public/flowUtilityTokenReceiver)
137    assert(merchantFUTReceiverRef.borrow() != nil, message: "Missing or mis-typed merchant FUT receiver")
138
139    // Deposit FUT to merchant account FUT Vault (it's then forwarded to the main FUT contract afterwards)
140    merchantFUTReceiverRef.borrow()!.deposit(from: <-paymentVault)
141
142    // Get buyer collection public to receive Wine NFT
143    let recipient = getAccount(buyer)
144    let NFTReceiver = recipient.getCapability(Wine.CollectionPublicPath)
145      .borrow<&{NonFungibleToken.CollectionPublic}>()
146      ?? panic("Could not get receiver reference to the NFT Collection")
147
148    // Mint Collector NFT
149    admin.mintNFT(recipient: NFTReceiver, setID: setID, templateID: templateID)
150
151    // Remove utilized spots
152    self.updateSaleAccountsForBuyer(
153      templateID: templateID,
154      buyer: buyer,
155      newValue: self.saleAccounts["PUBLIC"]![templateID]![buyer]! - 1
156    )
157  }
158
159  pub resource Admin {
160    pub fun updateSaleMaxTokens(newSaleMaxTokens: {String: {UInt64: UInt64}}) {
161      WineMinter.saleMaxTokens = newSaleMaxTokens
162    }
163
164    pub fun updateSalePrices(newSalePrices: {String: {UInt64: UFix64}}) {
165      WineMinter.salePrices = newSalePrices
166    }
167
168    pub fun updateMerchantAccounts(newMerchantAccounts: {String: Address}) {
169      WineMinter.merchantAccounts = newMerchantAccounts
170    }
171
172    pub fun pruneSaleAccounts() {
173      WineMinter.saleAccounts = {}
174    }
175  }
176
177  pub fun getSaleAccounts(): {String: {UInt64: {Address: UInt64}}} {
178    return self.saleAccounts
179  }
180
181  init() {
182    self.AdminStoragePath = /storage/CollectorMinterAdmin
183
184    self.saleMaxTokens = {}
185    self.salePrices = {}
186    self.saleAccounts = {}
187
188    // For DUC testnet this should be 0x03df89ac89a3d64a
189    // For DUC mainnet this should be 0xfe15c4f52a58c75e
190    self.merchantAccounts = {}
191
192    let admin <- create Admin()
193    self.account.save(<-admin, to: self.AdminStoragePath)
194
195    emit ContractInitialized()
196  }
197}
198