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