Smart Contract
WineMinter
A.fb27085fbb495d1d.WineMinter
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