Smart Contract

CrowdflixMarket

A.5a35c571a4feae1e.CrowdflixMarket

Valid From

125,008,195

Deployed

1w ago
Feb 19, 2026, 06:53:40 PM UTC

Dependents

2 imports
1/**
2
3    CrowdflixMarket.cdc
4
5    Description: Contract definitions for users to sell their moments
6
7    Marketplace is where users can create a sale collection that they
8    store in their account storage. In the sale collection, 
9    they can put their NFTs up for sale with a price and publish a 
10    reference so that others can see the sale.
11
12    If another user sees an NFT that they want to buy,
13    they can send fungible tokens that equal the buy price
14    to buy the NFT.  The NFT is transferred to them when
15    they make the purchase.
16
17    Each user who wants to sell tokens will have a sale collection 
18    instance in their account that contains price information 
19    for each node in their collection. The sale holds a capability that 
20    links to their main moment collection.
21
22    They can give a reference to this collection to a central contract
23    so that it can list the sales in a central place
24
25    When a user creates a sale, they will supply four arguments:
26    - A Crowdflix.Collection capability that allows their sale to withdraw
27      a moment when it is purchased.
28    - A FungibleToken.Receiver capability as the place where the payment for the token goes.
29    - A FungibleToken.Receiver capability specifying a beneficiary, where a cut of the purchase gets sent. 
30    - A cut percentage, specifying how much the beneficiary will recieve.
31    
32    The cut percentage can be set to zero if the user desires and they 
33    will receive the entirety of the purchase. Crowdflix will initialize sales 
34    for users with the Crowdflix admin vault as the vault where cuts get 
35    deposited to.
36**/
37
38// Testnet
39// import FungibleToken from 0x9a0766d93b6608b7
40// import NonFungibleToken from 0x631e88ae7f1d7c20
41// import MetadataViews from 0x631e88ae7f1d7c20
42// import Crowdflix from 0xa27ccee47a521b38
43// import FlowToken from 0x7e60df042a9c0868
44// Production
45import FungibleToken from 0xf233dcee88fe0abe
46import NonFungibleToken from 0x1d7e57aa55817448
47import MetadataViews from 0x1d7e57aa55817448
48import Crowdflix from 0xad9300d3ca41f63a
49import FlowToken from 0x1654653399040a61
50
51access(all) contract CrowdflixMarket {
52
53    // -----------------------------------------------------------------------
54    // Crowdflix Market contract Event definitions
55    // -----------------------------------------------------------------------
56
57    /// emitted when a Crowdflix moment is listed for sale
58    access(all) event MomentListed(id: UInt64, price: UFix64, seller: Address?)
59    /// emitted when a token is purchased from the market
60    access(all) event MomentPurchased(id: UInt64, price: UFix64, seller: Address?, momentName: String, momentDescription: String, momentThumbnailURL: String)
61    /// emitted when a moment has been withdrawn from the sale
62    access(all) event MomentWithdrawn(id: UInt64, owner: Address?)
63
64    /// Path where the `SaleCollection` is stored
65    access(all) let marketStoragePath: StoragePath
66
67    /// Path where the public capability for the `SaleCollection` is
68    access(all) let marketPublicPath: PublicPath
69
70    /// SaleCollection
71    ///
72    /// This is the main resource that token sellers will store in their account
73    /// to manage the NFTs that they are selling. The SaleCollection
74    /// holds a Crowdflix Collection resource to store the moments that are for sale.
75    /// The SaleCollection also keeps track of the price of each token.
76    /// 
77    /// When a token is purchased, a cut is taken from the tokens
78    /// and sent to the beneficiary, then the rest are sent to the seller.
79    ///
80    /// The seller chooses who the beneficiary is and what percentage
81    /// of the tokens gets taken from the purchase
82    access(all) resource SaleCollection {
83
84        /// A collection of the moments that the user has for sale
85        access(self) var ownerCollection: Capability<auth(NonFungibleToken.Withdraw, NonFungibleToken.Update) &Crowdflix.Collection>
86
87        /// Dictionary of the low low prices for each NFT by ID
88        access(self) var prices: {UInt64: UFix64}
89
90        /// The fungible token vault of the seller
91        /// so that when someone buys a token, the tokens are deposited
92        /// to this Vault
93        access(self) var ownerCapability: Capability<&{FungibleToken.Receiver}>
94
95        /// The capability that is used for depositing 
96        /// the beneficiary's cut of every sale
97        access(self) var beneficiaryCapability: Capability<&{FungibleToken.Receiver}>
98
99        /// The percentage that is taken from every purchase for the beneficiary
100        /// For example, if the percentage is 15%, cutPercentage = 0.15
101        access(all) var cutPercentage: UFix64
102
103        init (ownerCollection: Capability<auth(NonFungibleToken.Withdraw, NonFungibleToken.Update) &Crowdflix.Collection>,
104              ownerCapability: Capability<&{FungibleToken.Receiver}>,
105              beneficiaryCapability: Capability<&{FungibleToken.Receiver}>,
106              cutPercentage: UFix64) {
107            pre {
108                // Check that the owner's moment collection capability is correct
109                ownerCollection.check(): 
110                    "Owner's Moment Collection Capability is invalid!"
111
112                // Check that both capabilities are for fungible token Vault receivers
113                ownerCapability.check(): 
114                    "Owner's Receiver Capability is invalid!"
115                beneficiaryCapability.check(): 
116                    "Beneficiary's Receiver Capability is invalid!" 
117            }
118            
119            // create an empty collection to store the moments that are for sale
120            self.ownerCollection = ownerCollection
121            self.ownerCapability = ownerCapability
122            self.beneficiaryCapability = beneficiaryCapability
123            // prices are initially empty because there are no moments for sale
124            self.prices = {}
125            self.cutPercentage = cutPercentage
126        }
127
128        /// listForSale lists an NFT for sale in this sale collection
129        /// at the specified price
130        ///
131        /// Parameters: tokenID: The id of the NFT to be put up for sale
132        ///             price: The price of the NFT
133        access(all) fun listForSale(tokenID: UInt64, price: UFix64) {
134            pre {
135                self.ownerCollection.borrow()!.borrowMoment(id: tokenID) != nil:
136                    "Moment does not exist in the owner's collection"
137            }
138
139            // Set the token's price
140            self.prices[tokenID] = price
141
142            emit MomentListed(id: tokenID, price: price, seller: self.owner?.address)
143        }
144
145        /// cancelSale cancels a moment sale and clears its price
146        ///
147        /// Parameters: tokenID: the ID of the token to withdraw from the sale
148        ///
149        access(all) fun cancelSale(tokenID: UInt64) {
150            // Remove the price from the prices dictionary
151            self.prices.remove(key: tokenID)
152
153            // Set prices to nil for the withdrawn ID
154            self.prices[tokenID] = nil
155            
156            // Emit the event for withdrawing a moment from the Sale
157            emit MomentWithdrawn(id: tokenID, owner: self.owner?.address)
158        }
159
160        /// purchase lets a user send tokens to purchase an NFT that is for sale
161        /// the purchased NFT is returned to the transaction context that called it
162        ///
163        /// Parameters: tokenID: the ID of the NFT to purchase
164        ///             buyTokens: the fungible tokens that are used to buy the NFT
165        ///
166        /// Returns: @Crowdflix.NFT: the purchased NFT
167        access(all) fun purchase(tokenID: UInt64, buyTokens: @FlowToken.Vault): @Crowdflix.NFT {
168            if self.prices[tokenID] != nil {
169                assert(
170                    buyTokens.balance == self.prices[tokenID]!,
171                    message: "Not enough tokens to buy the NFT!"
172                )
173
174                // Read the price for the token
175                let price = self.prices[tokenID]!
176
177                // Set the price for the token to nil
178                self.prices[tokenID] = nil
179
180                // Take the cut of the tokens that the beneficiary gets from the sent tokens
181                let beneficiaryCut <- buyTokens.withdraw(amount: price*self.cutPercentage)
182
183                // Deposit it into the beneficiary's Vault
184                self.beneficiaryCapability.borrow()!
185                    .deposit(from: <-beneficiaryCut)
186                
187                // Deposit the remaining tokens into the owners vault
188                self.ownerCapability.borrow()!
189                    .deposit(from: <-buyTokens)
190
191                // Return the purchased token
192                let boughtMoment <- self.ownerCollection.borrow()!.withdraw(withdrawID: tokenID) as! @Crowdflix.NFT
193
194                let momentDisplay = boughtMoment.resolveView(Type<MetadataViews.Display>())! as! MetadataViews.Display
195                emit MomentPurchased(id: tokenID, price: price, seller: self.owner?.address, momentName: momentDisplay.name, momentDescription: momentDisplay.description, momentThumbnailURL: momentDisplay.thumbnail.uri())
196
197                return <-boughtMoment
198            }
199
200            panic("No token matching this ID for sale!")
201
202        }
203
204        /// changeOwnerReceiver updates the capability for the sellers fungible token Vault
205        ///
206        /// Parameters: newOwnerCapability: The new fungible token capability for the account 
207        ///                                 who received tokens for purchases
208        access(all) fun changeOwnerReceiver(_ newOwnerCapability: Capability<&{FungibleToken.Receiver}>) {
209            pre {
210                newOwnerCapability.borrow() != nil: 
211                    "Owner's Receiver Capability is invalid!"
212            }
213            self.ownerCapability = newOwnerCapability
214        }
215
216        /// changeBeneficiaryReceiver updates the capability for the beneficiary of the cut of the sale
217        ///
218        /// Parameters: newBeneficiaryCapability the new capability for the beneficiary of the cut of the sale
219        ///
220        access(all) fun changeBeneficiaryReceiver(_ newBeneficiaryCapability: Capability<&{FungibleToken.Receiver}>) {
221            pre {
222                newBeneficiaryCapability.borrow() != nil: 
223                    "Beneficiary's Receiver Capability is invalid!" 
224            }
225            self.beneficiaryCapability = newBeneficiaryCapability
226        }
227
228        /// getPrice returns the price of a specific token in the sale
229        /// 
230        /// Parameters: tokenID: The ID of the NFT whose price to get
231        ///
232        /// Returns: UFix64: The price of the token
233        access(all) view fun getPrice(tokenID: UInt64): UFix64? {
234            if let price = self.prices[tokenID] {
235                return price
236            }
237            return nil
238        }
239
240        /// getIDs returns an array of token IDs that are for sale
241        access(all) view fun getIDs(): [UInt64] {
242            return self.prices.keys
243        }
244
245        /// borrowMoment Returns a borrowed reference to a Moment for sale
246        /// so that the caller can read data from it
247        ///
248        /// Parameters: id: The ID of the moment to borrow a reference to
249        ///
250        /// Returns: &Crowdflix.NFT? Optional reference to a moment for sale 
251        ///                        so that the caller can read its data
252        ///
253        access(all) view fun borrowMoment(id: UInt64): &Crowdflix.NFT? {
254            let ref = self.ownerCollection.borrow()!.borrowMoment(id: id)
255            return ref
256        }
257    }
258
259    /// createCollection returns a new collection resource to the caller
260    access(all) fun createSaleCollection(ownerCollection: Capability<auth(NonFungibleToken.Withdraw, NonFungibleToken.Update) &Crowdflix.Collection>,
261                                 ownerCapability: Capability<&{FungibleToken.Receiver}>,
262                                 beneficiaryCapability: Capability<&{FungibleToken.Receiver}>,
263                                 cutPercentage: UFix64,): @SaleCollection {
264
265        return <- create SaleCollection(ownerCollection: ownerCollection,
266                                        ownerCapability: ownerCapability,
267                                        beneficiaryCapability: beneficiaryCapability,
268                                        cutPercentage: cutPercentage)
269    }
270
271    init() {
272        self.marketStoragePath = /storage/CrowdflixMarketCollection
273        self.marketPublicPath = /public/CrowdflixMarketCollection
274    }
275}
276