Smart Contract

BasketballsMarket

A.eee6bdee2b2bdfc8.BasketballsMarket

Deployed

5h ago
Mar 01, 2026, 01:38:18 AM UTC

Dependents

0 imports
1import Basketballs from 0xeee6bdee2b2bdfc8
2import FungibleToken from 0xf233dcee88fe0abe
3import NonFungibleToken from 0x1d7e57aa55817448
4
5/*
6    This is a simple Basketballs initial sale contract for the DApp to use
7    in order to list and sell Basketballs.
8
9    Its structure is neither what it would be if it was the simplest possible
10    marjet contract or if it was a complete general purpose market contract.
11    Rather it's the simplest possible version of a more general purpose
12    market contract that indicates how that contract might function in
13    broad strokes. This has been done so that integrating with this contract
14    is a useful preparatory exercise for code that will integrate with the
15    later more general purpose market contract.
16
17    It allows:
18    - Anyone to create Sale Offers and place them in a collection, making it
19      publicly accessible.
20    - Anyone to accept the offer and buy the item.
21
22    It notably does not handle:
23    - Multiple different sale NFT contracts.
24    - Multiple different payment FT contracts.
25    - Splitting sale payments to multiple recipients.
26
27 */
28
29access(all) contract BasketballsMarket {
30    // SaleOffer events.
31    //
32    // A sale offer has been created.
33    access(all) event SaleOfferCreated(itemID: UInt64, price: UFix64)
34    // Someone has purchased an item that was offered for sale.
35    access(all) event SaleOfferAccepted(itemID: UInt64)
36    // A sale offer has been destroyed, with or without being accepted.
37    access(all) event SaleOfferFinished(itemID: UInt64)
38
39    // Collection events.
40    //
41    // A sale offer has been inserted into the collection of Address.
42    access(all) event CollectionInsertedSaleOffer(saleItemID: UInt64, saleItemCollection: Address)
43    // A sale offer has been removed from the collection of Address.
44    access(all) event CollectionRemovedSaleOffer(saleItemID: UInt64, saleItemCollection: Address)
45
46    // Named paths
47    //
48    access(all) let CollectionStoragePath: StoragePath
49    access(all) let CollectionPublicPath: PublicPath
50
51    // SaleOfferPublicView
52    // An interface providing a read-only view of a SaleOffer
53    //
54    access(all) resource interface SaleOfferPublicView {
55        access(all) var saleCompleted: Bool
56        access(all) let saleItemID: UInt64
57        access(all) let salePrice: UFix64
58    }
59
60    access(all) struct SaleOfferView {
61        access(all) let saleItemID: UInt64
62        access(all) let salePrice: UFix64
63        access(all) let saleCompleted: Bool
64        access(all) let editionID: UInt32
65        access(all) let name: String
66        access(all) let description: String
67        access(all) let imageURL: String
68        access(all) let serialNumber: UInt64
69        access(all) let circulatingCount: UInt64
70
71        init(
72            saleItemID: UInt64,
73            salePrice: UFix64,
74            saleCompleted: Bool,
75            editionID: UInt32,
76            serialNumber: UInt64,
77            name: String,
78            description: String,
79            imageURL: String,
80            circulatingCount: UInt64
81        ) {
82            self.saleItemID = saleItemID
83            self.salePrice = salePrice
84            self.saleCompleted = saleCompleted
85            self.editionID = editionID
86            self.name = name
87            self.description = description
88            self.imageURL = imageURL
89            self.serialNumber = serialNumber
90            self.circulatingCount = circulatingCount
91        }
92    }
93
94    // SaleOffer
95    // A Basketballs NFT being offered to sale for a set fee
96    //
97    access(all) resource SaleOffer: SaleOfferPublicView {
98        // Whether the sale has completed with someone purchasing the item.
99        access(all) var saleCompleted: Bool
100
101        // The Basketballs NFT ID for sale.
102        access(all) let saleItemID: UInt64
103        // The collection containing that ID.
104        access(self) let sellerItemProvider: Capability<auth(NonFungibleToken.Withdraw) &Basketballs.Collection>
105
106        // The sale payment price.
107        access(all) let salePrice: UFix64
108      // The vault that will receive that payment if teh sale completes successfully.
109        access(self) let sellerPaymentReceiver: Capability<&{FungibleToken.Receiver}>
110
111        // Called by a purchaser to accept the sale offer.
112        // If they send the correct payment and if the item is still available,
113        // the Basketballs NFT will be placed in their Basketballs.Collection .
114        //
115        access(all) fun accept(
116            buyerCollection: &Basketballs.Collection,
117            buyerPayment: @{FungibleToken.Vault}
118        ) {
119            panic("Not implemented")
120        }
121
122        // initializer
123        // Take the information required to create a sale offer, notably the capability
124        // to transfer the Basketballs NFT and the capability to receive a fungible token in payment.
125        //
126        init(
127            sellerItemProvider: Capability<auth(NonFungibleToken.Withdraw) &Basketballs.Collection>,
128            saleItemID: UInt64,
129            sellerPaymentReceiver: Capability<&{FungibleToken.Receiver}>,
130            salePrice: UFix64
131        ) {
132            pre {
133                sellerItemProvider.borrow() != nil: "Cannot borrow seller"
134                sellerPaymentReceiver.borrow() != nil: "Cannot borrow sellerPaymentReceiver"
135            }
136
137            self.saleCompleted = false
138
139            self.sellerItemProvider = sellerItemProvider
140            self.saleItemID = saleItemID
141
142            self.sellerPaymentReceiver = sellerPaymentReceiver
143            self.salePrice = salePrice
144
145            emit SaleOfferCreated(itemID: self.saleItemID, price: self.salePrice)
146        }
147    }
148
149    // createSaleOffer
150    // Make creating a SaleOffer publicly accessible.
151    //
152    access(all) fun createSaleOffer (
153        sellerItemProvider: Capability<auth(NonFungibleToken.Withdraw) &Basketballs.Collection>,
154        saleItemID: UInt64,
155        sellerPaymentReceiver: Capability<&{FungibleToken.Receiver}>,
156        salePrice: UFix64
157    ): @SaleOffer {
158        return <-create SaleOffer(
159            sellerItemProvider: sellerItemProvider,
160            saleItemID: saleItemID,
161            sellerPaymentReceiver: sellerPaymentReceiver,
162            salePrice: salePrice
163        )
164    }
165
166    // CollectionManager
167    // An interface for adding and removing SaleOffers to a collection, intended for
168    // use by the collection's owner.
169    //
170    access(all) resource interface CollectionManager {
171        access(all) fun insert(offer: @BasketballsMarket.SaleOffer)
172        access(all) fun remove(saleItemID: UInt64): @SaleOffer
173    }
174
175        // CollectionPurchaser
176    // An interface to allow purchasing items via SaleOffers in a collection.
177    // This function is also provided by CollectionPublic, it is here to support
178    // more fine-grained access to the collection for as yet unspecified future use cases.
179    //
180    access(all) resource interface CollectionPurchaser {
181        access(all) fun purchase(
182            saleItemID: UInt64,
183            buyerCollection: &Basketballs.Collection,
184            buyerPayment: @{FungibleToken.Vault}
185        )
186    }
187
188    // CollectionPublic
189    // An interface to allow listing and borrowing SaleOffers, and purchasing items via SaleOffers in a collection.
190    //
191    access(all) resource interface CollectionPublic {
192        access(all) fun getSaleOfferIDs(): [UInt64]
193        access(all) fun borrowSaleItem(saleItemID: UInt64): &{SaleOfferPublicView}?
194        access(all) fun purchase(
195            saleItemID: UInt64,
196            buyerCollection: &Basketballs.Collection,
197            buyerPayment: @{FungibleToken.Vault}
198        )
199   }
200
201    // Collection
202    // A resource that allows its owner to manage a list of SaleOffers, and purchasers to interact with them.
203    //
204    access(all) resource Collection : CollectionManager, CollectionPurchaser, CollectionPublic {
205        access(all) var saleOffers: @{UInt64: SaleOffer}
206
207        // insert
208        // Insert a SaleOffer into the collection, replacing one with the same saleItemID if present.
209        //
210         access(all) fun insert(offer: @BasketballsMarket.SaleOffer) {
211            let id: UInt64 = offer.saleItemID
212
213            // add the new offer to the dictionary which removes the old one
214            let oldOffer <- self.saleOffers[id] <- offer
215            destroy oldOffer
216
217            emit CollectionInsertedSaleOffer(saleItemID: id, saleItemCollection: self.owner?.address!)
218        }
219
220        // remove
221        // Remove and return a SaleOffer from the collection.
222        access(all) fun remove(saleItemID: UInt64): @SaleOffer {
223            emit CollectionRemovedSaleOffer(saleItemID: saleItemID, saleItemCollection: self.owner?.address!)
224            return <-(self.saleOffers.remove(key: saleItemID) ?? panic("missing SaleOffer"))
225        }
226
227        // purchase
228        // If the caller passes a valid saleItemID and the item is still for sale, and passes a vault
229        // typed as a FungibleToken.Vault (SomeToken.deposit() handles should cast to concrete type)
230        // containing the correct payment amount, this will transfer the Basketball to the caller's
231        // Basketballs collection.
232        // It will then remove and destroy the offer.
233        // Note that is means that events will be emitted in this order:
234        //   1. Collection.CollectionRemovedSaleOffer
235        //   2. Basketballs.Withdraw
236        //   3. Basketballs.Deposit
237        //   4. SaleOffer.SaleOfferFinished
238        //
239        access(all) fun purchase(
240            saleItemID: UInt64,
241            buyerCollection: &Basketballs.Collection,
242            buyerPayment: @{FungibleToken.Vault}
243        ) {
244            pre {
245                self.saleOffers[saleItemID] != nil: "SaleOffer does not exist in the collection!"
246            }
247            let offer <- self.remove(saleItemID: saleItemID)
248            offer.accept(buyerCollection: buyerCollection, buyerPayment: <-buyerPayment)
249            //FIXME: Is this correct? Or should we return it to the caller to dispose of?
250            destroy offer
251        }
252
253        // getSaleOfferIDs
254        // Returns an array of the IDs that are in the collection
255        //
256        access(all) fun getSaleOfferIDs(): [UInt64] {
257            return self.saleOffers.keys
258        }
259
260        // borrowSaleItem
261        // Returns an Optional read-only view of the SaleItem for the given saleItemID if it is contained by this collection.
262        // The optional will be nil if the provided saleItemID is not present in the collection.
263        //
264        access(all) fun borrowSaleItem(saleItemID: UInt64): &{SaleOfferPublicView}? {
265            if self.saleOffers[saleItemID] == nil {
266                return nil
267            } else {
268                return &self.saleOffers[saleItemID]
269            }
270        }
271
272        // constructor
273        //
274        init () {
275            self.saleOffers <- {}
276        }
277    }
278
279    // createEmptyCollection
280    // Make creating a Collection publicly accessible.
281    //
282    access(all) fun createEmptyCollection(): @Collection {
283        return <-create Collection()
284    }
285
286    init () {
287        self.CollectionStoragePath = /storage/BasketballsMarketCollection
288        self.CollectionPublicPath = /public/BasketballsMarketCollection
289    }
290}