Smart Contract

MarketPlace

A.f5b0eb433389ac3f.MarketPlace

Deployed

14h ago
Feb 28, 2026, 04:34:50 AM UTC

Dependents

0 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import FlowToken from 0x1654653399040a61
3import Collectible from 0xf5b0eb433389ac3f
4import Edition from 0xf5b0eb433389ac3f
5import NonFungibleToken from 0x1d7e57aa55817448
6import FUSD from 0x3c5959b568896393
7
8pub contract MarketPlace {  
9
10    pub let CollectionStoragePath: StoragePath
11    pub let CollectionPublicPath: PublicPath
12
13    // Event that is emitted when a new NFT is put up for sale
14    pub event ForSale(id: UInt64, owner: Address, price: UFix64)
15
16    // Event that is emitted when the price of an NFT changes
17    pub event PriceChanged(id: UInt64, owner: Address, newPrice: UFix64, oldPrice: UFix64)    
18
19    // Event that is emitted when a token is purchased
20    pub event TokenPurchased(id: UInt64, price: UFix64, from:Address, to:Address)
21
22    // Event that is emitted when a seller withdraws their NFT from the sale
23    pub event SaleWithdrawn(id: UInt64, owner: Address)
24
25    // Secondary commission events
26    pub event Earned(nftID: UInt64, amount: UFix64, owner: Address, type: String)
27    pub event FailEarned(nftID: UInt64, amount: UFix64, owner: Address, type: String)
28
29    pub resource interface SaleCollectionPublic {
30        pub fun purchase(
31            tokenID: UInt64,
32            recipientCap: Capability<&{Collectible.CollectionPublic}>,
33            buyTokens: @FUSD.Vault     
34        )
35        pub fun idPrice(tokenID: UInt64): UFix64?
36        pub fun getIDs(): [UInt64]
37        pub fun borrowCollectible(id: UInt64): &Collectible.NFT?
38    }
39
40    // SaleCollection
41    //
42    // NFT Collection object that allows a user to put their NFT up for sale
43    // where others can send fungible tokens to purchase it
44    //
45    pub resource SaleCollection: SaleCollectionPublic {    
46
47        // Dictionary of the NFTs that the user is putting up for sale
48        access(self) var forSale: @{UInt64: Collectible.NFT}
49
50        // Dictionary of the prices for each NFT by ID
51        access(self) var prices: {UInt64: UFix64}
52
53        // The fungible token vault of the owner of this sale.
54        // When someone buys a token, this resource can deposit
55        // tokens into their account.
56        access(account) let ownerVault: Capability<&FUSD.Vault{FungibleToken.Receiver}>
57
58        init (vault: Capability<&FUSD.Vault{FungibleToken.Receiver}>) {
59            self.forSale <- {}
60            self.ownerVault = vault
61            self.prices = {}
62        }
63
64        priv fun getEditionNumber(id: UInt64): UInt64? {
65            let ref = self.borrowCollectible(id: id) 
66            if ref == nil { 
67                return nil 
68            }
69            return ref!.editionNumber
70        }
71
72        // withdraw gives the owner the opportunity to remove a sale from the collection
73        pub fun withdraw(tokenID: UInt64): @Collectible.NFT {
74            // remove the price
75            self.prices.remove(key: tokenID)
76            
77            // remove and return the token
78            let token <- self.forSale.remove(key: tokenID) ?? panic("missing NFT")
79
80            let vaultRef = self.ownerVault.borrow() ?? panic("Could not borrow reference to owner token vault")
81          
82            return <-token
83        }
84
85        // listForSale lists an NFT for sale in this collection
86        pub fun listForSale(token: @Collectible.NFT, price: UFix64) {
87            pre {              
88                price > 0.00 : "Price should be more than 0"  
89                price <= 999999.99 : "Price should be less than 1 000 000.00"     
90            }
91
92            let id = token.id
93
94            // store the price in the price array
95            self.prices[id] = price
96
97            // put the NFT into the the forSale dictionary
98            let oldToken <- self.forSale[id] <- token
99            destroy oldToken
100
101            let vaultRef = self.ownerVault.borrow() ?? panic("Could not borrow reference to owner token vault")
102
103            emit ForSale(id: id, owner: vaultRef.owner!.address, price: price)
104        }
105
106        // changePrice changes the price of a token that is currently for sale
107        pub fun changePrice(tokenID: UInt64, newPrice: UFix64) {
108            pre {
109                self.prices[tokenID] != nil : "NFT does not exist on sale"  
110                newPrice > 0.00 : "Price should be more than 0"  
111                newPrice <= 999999.99 : "Price should be less than 1 000 000.00"        
112            }
113
114            let oldPrice = self.prices[tokenID]!
115
116            self.prices[tokenID] = newPrice
117
118            let vaultRef = self.ownerVault.borrow() ?? panic("Could not borrow reference to owner token vault")
119
120            emit PriceChanged(id: tokenID, owner: vaultRef.owner!.address, newPrice: newPrice, oldPrice: oldPrice)
121        }
122
123        // remove nft from sale. this uses is to differ between cancel sale and buy NFT
124        pub fun withdrawFromSale(tokenID: UInt64): @Collectible.NFT {
125            pre {
126                self.prices[tokenID] != nil : "NFT does not exist on sale"          
127            }
128
129            let vaultRef = self.ownerVault.borrow() ?? panic("Could not borrow reference to owner token vault")
130
131            emit SaleWithdrawn(id: tokenID, owner: vaultRef.owner!.address)
132
133            return <-self.withdraw(tokenID: tokenID)  
134        }
135
136        // idPrice returns the price of a specific token in the sale
137        pub fun idPrice(tokenID: UInt64): UFix64? {
138            return self.prices[tokenID]
139        }
140
141        // getIDs returns an array of token IDs that are for sale
142        pub fun getIDs(): [UInt64] {
143            return self.forSale.keys
144        }
145
146        pub fun borrowCollectible(id: UInt64): &Collectible.NFT? {
147            if self.forSale[id] != nil {
148                let ref = &self.forSale[id] as auth &NonFungibleToken.NFT?
149                return ref as! &Collectible.NFT?
150            } else {
151                return nil
152            }
153        }   
154
155        priv fun handlePayments(
156            tokenID: UInt64,
157            buyTokens: @FUSD.Vault,
158            price: UFix64 
159        ) {
160            let vaultRef = self.ownerVault.borrow() ?? panic("Could not borrow reference to owner token vault")
161
162            // this is validated during process of the cration NFT
163            let editionNumber = self.getEditionNumber(id: tokenID)!  
164                      
165            let royaltyRef = MarketPlace.account.getCapability<&{Edition.EditionCollectionPublic}>(Edition.CollectionPublicPath).borrow()!             
166
167            let royaltyStatus = royaltyRef.getEdition(editionNumber)!           
168
169            for key in royaltyStatus.royalty.keys {
170                let commission = price * royaltyStatus.royalty[key]!.secondSalePercent * 0.01
171
172                let account = getAccount(key) 
173
174                let vaultCap = account.getCapability<&FUSD.Vault{FungibleToken.Receiver}>(/public/fusdReceiver)
175
176                if(vaultCap.check()) {
177                    let vaultCommissionRecepientRef = vaultCap.borrow()!
178
179                    vaultCommissionRecepientRef.deposit(from: <- buyTokens.withdraw(amount: commission))
180
181                    emit Earned(nftID: tokenID, amount: commission, owner: key, type: royaltyStatus.royalty[key]!.description)                   
182                } else {
183                   emit FailEarned(nftID: tokenID, amount: commission, owner: key, type: royaltyStatus.royalty[key]!.description)
184                }             
185            }
186
187            let amount = buyTokens.balance
188
189            // deposit the purchasing tokens into the owners vault
190            vaultRef.deposit(from: <- (buyTokens as! @FungibleToken.Vault))
191
192            emit Earned(nftID: tokenID, amount: amount, owner: vaultRef.owner!.address, type: "SELLER") 
193        }
194
195        // purchase lets a user send tokens to purchase an NFT that is for sale
196        pub fun purchase(
197            tokenID: UInt64,
198            recipientCap: Capability<&{Collectible.CollectionPublic}>,
199            buyTokens: @FUSD.Vault
200        ) {
201            pre {
202                self.forSale[tokenID] != nil && self.prices[tokenID] != nil: "No token matching this ID for sale!"
203                buyTokens.balance == (self.prices[tokenID] ?? 0.0): "Not exact amount tokens to buy the NFT!"
204            }         
205
206            let vaultRef = self.ownerVault.borrow() ?? panic("Could not borrow reference to owner token vault")
207
208            // get the value out of the optional
209            let price = self.prices[tokenID]!
210            
211            self.prices[tokenID] = nil       
212
213            let recipient = recipientCap.borrow() ?? panic("Could not borrow reference to buyer NFT storage")   
214
215            // Send money to seller and share the secondary commission
216            self.handlePayments(tokenID: tokenID, buyTokens: <- buyTokens, price: price)  
217            
218            let token <- self.withdraw(tokenID: tokenID)   
219
220            // deposit the NFT into the buyers collection
221            recipient.deposit(token: <- token)
222
223            emit TokenPurchased(id: tokenID, price: price, from: vaultRef.owner!.address, to: recipient.owner!.address)
224        }      
225
226        destroy() {
227            destroy self.forSale
228        }
229    }
230
231    // structure for display NFTs data
232    pub struct SaleData {
233        pub let metadata: Collectible.Metadata
234        pub let id: UInt64  
235        pub let price: UFix64?
236        pub let editionNumber: UInt64
237        init(metadata: Collectible.Metadata, id: UInt64, price: UFix64?, editionNumber: UInt64) {
238            self.metadata = metadata
239            self.id = id 
240            self.price = price
241            self.editionNumber = editionNumber
242        }
243    }
244
245    // get info for NFT including metadata
246    pub fun getSaleDatas(address: Address) : [SaleData] {
247
248        var saleData: [SaleData] = []
249        let account = getAccount(address)
250
251        let CollectibleCollection = account.getCapability<&AnyResource{MarketPlace.SaleCollectionPublic}>(MarketPlace.CollectionPublicPath)
252            .borrow()
253            ?? panic("Could not borrow sale reference")
254     
255            for id in CollectibleCollection.getIDs() {
256                var Collectible = CollectibleCollection.borrowCollectible(id: id) 
257                var price = CollectibleCollection.idPrice(tokenID: id)
258                saleData.append(SaleData(
259                    metadata: Collectible!.metadata,
260                    id: id,
261                    price: price,
262                    editionNumber: Collectible!.editionNumber             
263                ))
264            }        
265
266        return saleData
267    }      
268
269    // createCollection returns a new collection resource to the caller
270    pub fun createSaleCollection(ownerVault: Capability<&FUSD.Vault{FungibleToken.Receiver}>): @SaleCollection {
271        return <- create SaleCollection(vault: ownerVault)
272    }
273
274    init() {
275        self.CollectionPublicPath = /public/NFTbloctoXtinglesCollectibleSale
276        self.CollectionStoragePath = /storage/NFTbloctoXtinglesCollectibleSale
277    }
278}
279