Smart Contract

DisruptArtMarketplace

A.cd946ef9b13804c6.DisruptArtMarketplace

Deployed

5h ago
Mar 01, 2026, 07:12:18 AM UTC

Dependents

0 imports
1// DisruptArt NFT Marketplace
2// Marketplace smart contract
3// NFT Marketplace : www.disrupt.art
4// Owner           : Disrupt Art, INC.
5// Developer       : www.blaze.ws
6// Version         : 0.0.5
7// Blockchain      : Flow www.onFlow.org
8
9
10// FUSD FUNGIBLE TOKEN mainnet
11// 
12import FungibleToken from 0xf233dcee88fe0abe
13// Testnet wallet address
14import DisruptArt from 0xcd946ef9b13804c6
15
16// This contract allows users to list their DisruptArt NFT for sale. 
17// Other users can purchase these NFTs with FUSD token.
18
19pub contract DisruptArtMarketplace {
20
21  // Event that is emitted when a new Disruptnow NFT is put up for sale
22  pub event ForSale(id: UInt64, price: UFix64)
23
24  // Event that is emitted when the NFT price changes
25  pub event PriceChanged(id: UInt64, newPrice: UFix64)
26
27  // Event that is emitted when a NFT token is purchased
28  pub event TokenPurchased(id: UInt64, price: UFix64)
29
30  // Event that is emitted when a seller withdraws their NFT from the sale
31  pub event SaleWithdrawn(id: UInt64)
32
33  // Market operator Address 
34  pub var marketAddress : Address
35  // Market operator fee percentage 
36  pub var marketFee : UFix64
37  // creator royality
38  pub var royality : UFix64
39
40  /// Path where the `SaleCollection` is stored
41  pub let marketStoragePath: StoragePath
42
43  /// Path where the public capability for the `SaleCollection` is
44  pub let marketPublicPath: PublicPath
45    
46
47  // Interface public methods that are used for NFT sales actions
48
49  pub resource interface SalePublic {
50    pub fun purchase(id: UInt64, recipient: &{DisruptArt.DisruptArtCollectionPublic}, payment: @FungibleToken.Vault)
51    pub fun purchaseGroup(tokens: [UInt64], recipient: &{DisruptArt.DisruptArtCollectionPublic}, payment: @FungibleToken.Vault)
52    pub fun idPrice(id: UInt64): UFix64?
53    pub fun tokenCreator(id: UInt64): Address??
54    pub fun currentTokenOwner(id: UInt64): Address??
55    pub fun isResale(id: UInt64): Bool?
56    pub fun getIDs(): [UInt64]
57  }
58
59  // SaleCollection
60  //
61  // NFT Collection object that allows a user to list their NFT for sale
62  // where others can send fungible tokens to purchase it
63  //
64  pub resource SaleCollection: SalePublic {
65
66    // Dictionary of the NFTs that the user is putting up for sale
67    pub var forSale: @{UInt64: DisruptArt.NFT}
68
69    // Dictionary of the prices for each NFT by ID
70    pub var prices: {UInt64: UFix64}
71
72    // Dictionary of the prices for each NFT by ID
73    pub var creators: {UInt64: Address?}
74
75    // Dictionary of sale type (either sale/resale)
76    pub var resale: {UInt64: Bool}
77
78    // Dictionary of token owner
79    pub var seller : {UInt64: Address?}
80
81    // The fungible token vault of the owner of this sale.
82    // When someone buys a token, this resource can deposit
83    // tokens into their account.
84    access(account) let ownerVault: Capability<&AnyResource{FungibleToken.Receiver}>
85
86    init (vault: Capability<&AnyResource{FungibleToken.Receiver}>) {
87        self.forSale <- {}
88        self.ownerVault = vault
89        self.prices = {}
90        self.creators = {}
91        self.resale = {}
92        self.seller = {}
93    }
94
95    // Withdraw gives the owner the opportunity to remove a NFT from sale
96    pub fun withdraw(id: UInt64): @DisruptArt.NFT {
97        // remove the price
98        self.prices.remove(key: id)
99        self.creators.remove(key: id)
100        self.resale.remove(key: id)
101        self.seller.remove(key: id)
102        // remove and return the token
103        let token <- self.forSale.remove(key: id) ?? panic("missing NFT")
104        return <-token
105    }
106     
107    // List NFTs in market collection
108    pub fun listForSaleGroup(sellerRef: &DisruptArt.Collection, tokens: [UInt64], price: UFix64) {
109       
110        pre {
111            tokens.length > 0:
112                "No Token found. Please provide tokens!"
113            self.tokensExist(tokens: tokens, ids: sellerRef.getIDs()):
114                "Token is not available!"
115        }
116
117        var count = 0
118        while count < tokens.length {
119            let token <- sellerRef.withdraw(withdrawID: tokens[count]) as! @DisruptArt.NFT
120            self.listForSale(token: <- token, price: price)
121            count = count + 1
122        }
123    }
124
125    // lists an NFT for sale in this collection
126    pub fun listForSale(token: @DisruptArt.NFT, price: UFix64) {
127        let id = token.id
128
129        // Store the price in the price array
130        self.prices[id] = price
131
132        self.creators[id] = token.creator
133
134        self.seller[id] = self.owner?.address
135
136        self.resale[id] = (token.creator == self.owner?.address) ? false : true
137
138        // Put NFT into the forSale dictionary
139        let oldToken <- self.forSale[id] <- token
140        destroy oldToken
141
142        emit ForSale(id: id, price: price)
143    }
144
145    // Change price of a group of tokens
146    pub fun changePrice(tokens: [UInt64],newPrice: UFix64) {        
147        pre {
148            tokens.length > 0:
149                "Please provide tokens!"
150            self.tokensExist(tokens:tokens, ids: self.forSale.keys):
151                "Tokens not available"
152        }
153        var count = 0
154        while count < tokens.length {
155            self.prices[tokens[count]] = newPrice
156            emit PriceChanged(id: tokens[count], newPrice: newPrice)
157            count = count + 1
158        }
159    }
160
161    pub fun tokensExist(tokens: [UInt64], ids: [UInt64]):Bool {
162        var count = 0
163        // let ids = self.forSale.keys
164        while count < tokens.length {
165            if(ids.contains(tokens[count])) {
166                count = count + 1
167            } else {
168                return false
169            } 
170        }
171        return true
172    }
173
174    pub fun tokensTotalPrice(tokens: [UInt64]):UFix64 {
175        var count = 0
176        var price:UFix64 = 0.0 
177        while count < tokens.length {
178            price = price + self.prices[tokens[count]]!
179            count = count + 1
180        }
181        return price
182    }
183
184    // Purchase : Allows am user send FUSD to purchase a NFT that is for sale
185    pub fun purchaseGroup(tokens: [UInt64], recipient: &{DisruptArt.DisruptArtCollectionPublic}, payment: @FungibleToken.Vault) {
186       
187        pre {
188            tokens.length > 0:
189                "Please provide tokens!"
190            self.tokensExist(tokens: tokens, ids: self.forSale.keys):
191                "Tokens are not available"
192            payment.balance >= self.tokensTotalPrice(tokens:tokens) :
193                "Not enough FUSD to purchase this NFT token"
194        }
195
196        let totalTokens = UFix64(tokens.length)
197
198        var count = 0
199        while count < tokens.length {
200           let tokenCut <- payment.withdraw(amount:  self.prices[tokens[count]]!)
201           self.purchase(id: tokens[count], recipient:recipient, payment: <-tokenCut)
202           count = count + 1
203        }
204        
205        let disruptartvaultRef =  getAccount(DisruptArtMarketplace.marketAddress)
206                                  .getCapability(/public/fusdReceiver)
207                                  .borrow<&{FungibleToken.Receiver}>()
208                                  ?? panic("failed to borrow reference to DisruptArtMarketplace vault")
209
210        disruptartvaultRef.deposit(from: <-payment)
211    }
212
213
214    // Purchase: Allows an user to send FUSD to purchase a NFT that is for sale
215    pub fun purchase(id: UInt64, recipient: &{DisruptArt.DisruptArtCollectionPublic}, payment: @FungibleToken.Vault) {
216        pre {
217            self.forSale[id] != nil && self.prices[id] != nil:
218                "No token matching this ID for sale!"
219            payment.balance >= self.prices[id]! :
220                "Not enough FUSD to purchase NFT"
221        }
222     
223        let totalamount = payment.balance
224
225        let owner = self.creators[id]!!
226
227        // Disruptvalut reference
228        let disruptartvaultRef =  getAccount(DisruptArtMarketplace.marketAddress)
229                                  .getCapability(/public/fusdReceiver)
230                                .borrow<&{FungibleToken.Receiver}>()
231                                ?? panic("failed to borrow reference to DisruptArtMarketplace vault")
232        // Creator vault reference
233        let creatorvaultRef =  getAccount(owner)
234                                 .getCapability(/public/fusdReceiver)
235                                .borrow<&{FungibleToken.Receiver}>()
236                                ?? panic("failed to borrow reference to owner vault") 
237       
238        // Seller vault reference
239        let vaultRef = self.ownerVault.borrow()
240            ?? panic("Could not borrow reference to owner token vault")
241
242        let marketShare = self.flowshare(price:self.prices[id]!, commission:DisruptArtMarketplace.marketFee)
243        let royalityShare = self.flowshare(price:self.prices[id]!, commission:DisruptArtMarketplace.royality)
244        
245        let marketCut <- payment.withdraw(amount: marketShare)
246        let royalityCut <- payment.withdraw(amount: royalityShare)
247
248        disruptartvaultRef.deposit(from: <-marketCut)
249        // Royalty to the original creator / minted user if this is a resale.
250        if(self.resale[id]!) {
251            creatorvaultRef.deposit(from: <-royalityCut)
252        } else {
253            disruptartvaultRef.deposit(from: <-royalityCut)
254        }
255
256        // After Marketplace fee & royalty fee deposit the balance FUSD to seller's wallet
257        vaultRef.deposit(from: <-payment)
258
259        // Deposit the NFT token into the buyer's collection storage
260        recipient.deposit(token: <-self.withdraw(id: id))
261
262        self.prices[id] = nil
263        self.creators[id] = nil
264        self.resale[id] = nil
265        self.seller[id] = nil
266
267        emit TokenPurchased(id: id, price: totalamount)
268    }
269
270    pub fun flowshare(price:UFix64, commission:UFix64):UFix64 {
271            return (price / 100.0 ) * commission
272    }
273
274    // Return the FUSD price of a specific token that is listed for sale.
275    pub fun idPrice(id: UInt64): UFix64? {
276        return self.prices[id]
277    }
278
279    pub fun isResale(id: UInt64): Bool? {
280        return self.resale[id]
281    }
282
283    // Returns the owner / original minter of a specific NFT token in the sale
284    pub fun tokenCreator(id: UInt64): Address?? {
285        return self.creators[id]
286    }
287
288    pub fun currentTokenOwner(id: UInt64): Address?? {
289        return self.seller[id]
290    }
291
292    // getIDs returns an array of token IDs that are for sale
293    pub fun getIDs(): [UInt64] {
294        return self.forSale.keys
295    }
296
297    // Withdraw NFTs from DisruptArtMarketplace
298    pub fun saleWithdrawn(tokens : [UInt64]) {
299       pre {
300            tokens.length > 0:
301                "Please provide NFT tokens!"
302            self.tokensExist(tokens:tokens, ids: self.forSale.keys):
303                "Tokens are not available"
304       }
305
306       var count = 0
307       let owner = self.seller[tokens[0]]!!
308       let receiver = getAccount(owner)
309              .getCapability(DisruptArt.disruptArtPublicPath)
310              .borrow<&{DisruptArt.DisruptArtCollectionPublic}>()
311               ?? panic("Could not get receiver reference to the NFT Collection")
312
313       while count < tokens.length {
314          // Deposit the NFT back into the seller collection
315          receiver.deposit(token: <-self.withdraw(id: tokens[count]))
316          emit SaleWithdrawn(id: tokens[count])
317          count = count + 1
318       }
319    } 
320
321    destroy() {
322        destroy self.forSale
323    }
324  }
325
326  // createCollection returns a new collection resource to the caller
327  pub fun createSaleCollection(ownerVault: Capability<&AnyResource{FungibleToken.Receiver}>): @SaleCollection {
328    return <- create SaleCollection(vault: ownerVault)
329  }
330
331  pub resource Admin {
332    pub fun changeoperator(newOperator: Address, marketCommission: UFix64, royality: UFix64 ) {
333        DisruptArtMarketplace.marketAddress = newOperator
334        DisruptArtMarketplace.marketFee = marketCommission
335        DisruptArtMarketplace.royality = royality
336    } 
337  }
338
339  init() {
340    self.marketAddress = 0x1592be4ab7835516
341    // 5% Marketplace Fee
342    self.marketFee = 5.0
343    // 10% Royalty reward for original creater / minter for every re-sale
344    self.royality = 10.0
345
346    self.marketPublicPath = /public/DisruptArtNFTSale
347
348    self.marketStoragePath = /storage/DisruptArtNFTSale
349
350    self.account.save(<- create Admin(), to:/storage/DisruptArtMarketAdmin)
351  } 
352
353}
354