Smart Contract
MainStorefrontX1
A.66b60643244a7738.MainStorefrontX1
1// Mainnet
2import NonFungibleToken from 0x1d7e57aa55817448
3import FungibleToken from 0xf233dcee88fe0abe
4
5access(all) contract MainStorefrontX1 {
6
7 access(all) event MainStorefrontInitialized()
8 access(all) event StorefrontInitialized(storefrontResourceID: UInt64)
9 access(all) event StorefrontDestroyed(storefrontResourceID: UInt64)
10
11 access(all) event ListingAvailable(
12 storefrontAddress: Address,
13 listingResourceID: UInt64,
14 nftType: Type,
15 nftIDs: [UInt64],
16 ftVaultType: Type,
17 price: UFix64
18 )
19
20 access(all) event ListingCompleted(
21 listingResourceID: UInt64,
22 storefrontResourceID: UInt64,
23 purchased: Bool,
24 nftType: Type,
25 nftIDs: [UInt64]
26 )
27
28 access(all) let StorefrontStoragePath: StoragePath
29 access(all) let StorefrontPublicPath: PublicPath
30
31 access(all) struct ListingDetails {
32
33 access(all) var storefrontID: UInt64
34 // Whether this listing has been purchased or not.
35 access(all) var purchased: Bool
36 // The Type of the NonFungibleToken.NFT that is being listed.
37 access(all) let nftType: Type
38 // The ID of the NFT within that type.
39 access(all) let nftIDs: [UInt64]
40 // The Type of the FungibleToken that payments must be made in.
41 access(all) let salePaymentVaultType: Type
42 // The amount that must be paid in the specified FungibleToken.
43 access(all) let salePrice: UFix64
44
45 access(all) let receiver: Capability<&{FungibleToken.Receiver}>
46
47 access(all) let discount: UFix64?
48
49 // setToPurchased
50 // Irreversibly set this listing as purchased.
51 //
52 access(contract) fun setToPurchased() {
53 self.purchased = true
54 }
55
56 // initializer
57 //
58 init (
59 nftType: Type,
60 nftIDs: [UInt64],
61 salePaymentVaultType: Type,
62 storefrontID: UInt64,
63 salePrice: UFix64,
64 receiver: Capability<&{FungibleToken.Receiver}>,
65 discount: UFix64?
66 ) {
67 self.storefrontID = storefrontID
68 self.purchased = false
69 self.nftType = nftType
70 self.nftIDs = nftIDs
71 self.salePaymentVaultType = salePaymentVaultType
72 self.salePrice = salePrice
73 self.receiver = receiver
74 //Store the discounts
75 self.discount = discount
76 }
77 }
78
79 // ListingPublic
80 // An interface providing a useful access(all)lic interface to a Listing.
81 //
82 access(all) resource interface ListingPublic {
83 // borrowNFT
84 // This will assert in the same way as the NFT standard borrowNFT()
85 // if the NFT is absent, for example if it has been sold via another listing.
86 //
87 // access(all) fun borrowNFT(): &NonFungibleToken.NFT
88
89 // purchase
90 // Purchase the listing, buying the token.
91 // This pays the beneficiaries and returns the token to the buyer.
92 //
93 access(all) fun purchase(payment: @{FungibleToken.Vault}, collection: &{NonFungibleToken.CollectionPublic}): @{NonFungibleToken.NFT}?
94
95 // getDetails
96 //
97 access(all) fun getDetails(): ListingDetails
98
99 }
100
101 // Listing
102 // A resource that allows an NFT to be sold for an amount of a given FungibleToken,
103 // and for the proceeds of that sale to be split between several recipients.
104 access(all) resource Listing: ListingPublic {
105 // The simple (non-Capability, non-complex) details of the sale
106 access(self) let details: ListingDetails
107
108 /// A capability allowing this resource to withdraw the NFT with the given ID from its collection.
109 /// This capability allows the resource to withdraw *any* NFT, so you should be careful when giving
110 /// such a capability to a resource and always check its code to make sure it will use it in the
111 /// way that it claims.
112 access(contract) let nftProviderCapability: Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>
113
114 // borrowNFT
115 // This will assert in the same way as the NFT standard borrowNFT()
116 // if the NFT is absent, for example if it has been sold via another listing.
117 /*
118 access(all) fun borrowNFT(): &NonFungibleToken.NFT {
119 let ref = self.nftProviderCapability.borrow()!.borrowNFT(id: self.getDetails().nftIDs)
120 //- CANNOT DO THIS IN PRECONDITION: "member of restricted type is not accessible: isInstance"
121 // result.isInstance(self.getDetails().nftType): "token has wrong type"
122 assert(ref.isInstance(self.getDetails().nftType), message: "token has wrong type")
123 assert(ref.id == self.getDetails().nftIDs, message: "token has wrong ID")
124 return (ref as &NonFungibleToken.NFT?)!
125 }*/
126
127
128 // getDetails
129 // Get the details of the current state of the Listing as a struct.
130 access(all) fun getDetails(): ListingDetails {
131 return self.details
132 }
133
134 // Purchase the listing, buying the token.
135 //The purchase function receives payment and capability from the collection to which the NFT is to be sent.
136 //This checks the user's address and determines whether they are entitled to a discount
137 access(all) fun purchase(
138 payment: @{FungibleToken.Vault},
139 collection: &{NonFungibleToken.CollectionPublic}
140 ): @{NonFungibleToken.NFT}? {
141 pre {
142 self.details.purchased == false: "This listing has already been purchased."
143 payment.balance >= self.details.salePrice: "Insufficient funds to purchase the listing."
144 }
145
146 var nfts: @{NonFungibleToken.NFT}? <- nil
147
148 for nftID in self.details.nftIDs {
149 let nft <- self.nftProviderCapability.borrow()!.withdraw(withdrawID: nftID)
150 assert(nft.isInstance(self.details.nftType), message: "Withdrawn NFT is not of the expected type.")
151 collection.deposit(token: <- nft)
152 }
153
154 self.details.receiver.borrow()!.deposit(from: <- payment)
155 self.details.setToPurchased()
156
157 emit ListingCompleted(
158 listingResourceID: self.uuid,
159 storefrontResourceID: self.details.storefrontID,
160 purchased: true,
161 nftType: self.details.nftType,
162 nftIDs: self.details.nftIDs // Emitting all NFT IDs involved in the listing
163 )
164
165 return <- nfts // Return the collected NFTs (if needed)
166 }
167
168
169 // initializer
170 init (
171 nftProviderCapability: Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>,
172 nftType: Type,
173 nftIDs: [UInt64],
174 salePaymentVaultType: Type,
175 salePrice: UFix64,
176 storefrontID: UInt64,
177 receiver: Capability<&{FungibleToken.Receiver}>,
178 discount: UFix64?
179 ) {
180 // Store the sale information
181 self.details = ListingDetails(
182 nftType: nftType,
183 nftIDs: nftIDs,
184 salePaymentVaultType: salePaymentVaultType,
185 storefrontID: storefrontID,
186 salePrice: salePrice,
187 receiver: receiver,
188 discount: discount
189 )
190
191 // Store the NFT provider
192 self.nftProviderCapability = nftProviderCapability
193
194 // Check that the provider contains the NFT.
195 // We will check it again when the token is sold.
196 // We cannot move this into a function because initializers cannot call member functions.
197 let provider = self.nftProviderCapability.borrow()
198 assert(provider != nil, message: "cannot borrow nftProviderCapability")
199
200 // This will precondition assert if the token is not available.
201/*
202 let nft = provider!.borrowNFT(id: self.details.nftIDs)
203 assert(nft.isInstance(self.details.nftType), message: "token is not of specified type")*/
204
205 }
206 }
207
208 // StorefrontManager
209 // An interface for adding and removing Listings within a Storefront,
210 // intended for use by the Storefront's own
211 //
212 access(all) resource interface StorefrontManager {
213 // createListing
214 // Allows the Storefront owner to create and insert Listings.
215 //
216 access(all) fun createListing(
217 nftProviderCapability: Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>,
218 nftType: Type,
219 nftIDs: [UInt64],
220 receiver: Capability<&{FungibleToken.Receiver}>,
221 salePaymentVaultType: Type,
222 salePrice: UFix64,
223 discount: UFix64?
224 ): UInt64
225 // removeListing
226 // Allows the Storefront owner to remove any sale listing, acepted or not.
227 //
228 access(all) fun removeListing(listingResourceID: UInt64)
229 }
230
231 // StorefrontPublic
232 // An interface to allow listing and borrowing Listings, and purchasing items via Listings
233 // in a Storefront.
234 //
235 access(all) resource interface StorefrontPublic {
236 access(all) fun getListingIDs(): [UInt64]
237 access(all) fun borrowListing(listingResourceID: UInt64): &{ListingPublic}?
238 access(all) fun cleanup(listingResourceID: UInt64)
239 }
240
241 // Storefront
242 // A resource that allows its owner to manage a list of Listings, and anyone to interact with them
243 // in order to query their details and purchase the NFTs that they represent.
244 //
245 access(all) resource Storefront : StorefrontManager, StorefrontPublic {
246 // The dictionary of Listing uuids to Listing resources.
247 access(self) var listings: @{UInt64: Listing}
248
249 // insert
250 // Create and publish a Listing for an NFT.
251 //
252 access(all) fun createListing(
253 nftProviderCapability: Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>,
254 nftType: Type,
255 nftIDs: [UInt64],
256 receiver: Capability<&{FungibleToken.Receiver}>,
257 salePaymentVaultType: Type,
258 salePrice: UFix64,
259 discount: UFix64?
260 ): UInt64 {
261 let listing <- create Listing(
262 nftProviderCapability: nftProviderCapability,
263 nftType: nftType,
264 nftIDs: nftIDs,
265 salePaymentVaultType: salePaymentVaultType,
266 salePrice: salePrice,
267 storefrontID: self.uuid,
268 receiver: receiver,
269 discount: discount
270 )
271
272 let listingResourceID = listing.uuid
273 let listingPrice = listing.getDetails().salePrice
274
275 // Add the new listing to the dictionary.
276 let oldListing <- self.listings[listingResourceID] <- listing
277 // Note that oldListing will always be nil, but we have to handle it.
278
279 destroy oldListing
280
281 emit ListingAvailable(
282 storefrontAddress: self.owner?.address!,
283 listingResourceID: listingResourceID,
284 nftType: nftType,
285 nftIDs: nftIDs,
286 ftVaultType: salePaymentVaultType,
287 price: listingPrice
288 )
289
290 return listingResourceID
291 }
292
293
294 // removeListing
295 // Remove a Listing that has not yet been purchased from the collection and destroy it.
296 //
297 access(all) fun removeListing(listingResourceID: UInt64) {
298 let listing <- self.listings.remove(key: listingResourceID)
299 ?? panic("missing Listing")
300
301 // This will emit a ListingCompleted event.
302 destroy listing
303 }
304
305 // getListingIDs
306 // Returns an array of the Listing resource IDs that are in the collection
307 //
308 access(all) fun getListingIDs(): [UInt64] {
309 return self.listings.keys
310 }
311
312 // borrowSaleItem
313 // Returns a read-only view of the SaleItem for the given listingID if it is contained by this collection.
314 //
315 access(all) fun borrowListing(listingResourceID: UInt64): &{ListingPublic}? {
316 if self.listings[listingResourceID] != nil {
317 return (&self.listings[listingResourceID] as &{ListingPublic}?)
318 } else {
319 return nil
320 }
321 }
322
323 // cleanup
324 // Remove an listing *if* it has been purchased.
325 // Anyone can call, but at present it only benefits the account owner to do so.
326 // Kind purchasers can however call it if they like.
327 //
328 access(all) fun cleanup(listingResourceID: UInt64) {
329 pre {
330 self.listings[listingResourceID] != nil: "could not find listing with given id"
331 }
332
333 let listing <- self.listings.remove(key: listingResourceID)!
334 assert(listing.getDetails().purchased == true, message: "listing is not purchased, only admin can remove")
335 destroy listing
336 }
337
338
339 // constructor
340 //
341 init () {
342 self.listings <- {}
343
344 // Let event consumers know that this storefront exists
345 emit StorefrontInitialized(storefrontResourceID: self.uuid)
346 }
347 }
348
349 // createStorefront
350 // Make creating a Storefront publicly accessible.
351 //
352 access(all) fun createStorefront(): @Storefront {
353 return <-create Storefront()
354 }
355
356 init () {
357 self.StorefrontStoragePath = /storage/MainStorefrontX
358 self.StorefrontPublicPath = /public/MainStorefrontX
359
360 emit MainStorefrontInitialized()
361 }
362
363
364
365}