Smart Contract

DisruptArtAuctionFlow

A.cd946ef9b13804c6.DisruptArtAuctionFlow

Deployed

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

Dependents

0 imports
1// DisruptArt NFT Marketplace
2// Auction smart contract
3// NFT Marketplace : www.disrupt.art
4// Owner           : Disrupt Art, INC.
5// Developer       : www.blaze.ws
6// Version         : 0.0.1
7// Blockchain      : Flow www.onFlow.org
8
9import FungibleToken from 0xf233dcee88fe0abe
10import DisruptArt from 0xcd946ef9b13804c6
11import NonFungibleToken from 0x1d7e57aa55817448
12import DisruptArtMarketplaceFlow from 0xcd946ef9b13804c6
13import DisruptArtAuction from 0xcd946ef9b13804c6
14
15pub contract DisruptArtAuctionFlow {
16
17    // The total amount of AuctionItems that have been created
18    pub var totalAuctions: UInt64
19
20    // Events
21    pub event TokenAddedToAuctionItems(auctionID: UInt64, startPrice: UFix64, minimumBidIncrement: UFix64, auctionStartBlock: UInt64, tokenID: UInt64, endTime:Fix64)
22    pub event NewBid(auctionID: UInt64, bidPrice: UFix64, bidder: Address?)
23    pub event AuctionSettled(auctionID: UInt64, price: UFix64)
24    pub event Canceled(auctionID: UInt64)
25
26    // Auction Storage Path
27    pub let auctionStoragePath: StoragePath
28
29    /// Auction Public Path
30    pub let auctionPublicPath: PublicPath
31
32    // This struct aggreates status for the auction and is exposed in order to create websites using auction information
33    pub struct AuctionStatus{
34        pub let id: UInt64
35        pub let price : UFix64
36        pub let bidIncrement : UFix64
37        pub let bids : UInt64
38        pub let active: Bool
39        pub let endTime : Fix64
40        pub let startTime : Fix64
41        pub let artId: UInt64?
42        pub let owner: Address
43        pub let leader: Address?
44        pub let completed: Bool
45        pub let expired: Bool
46    
47        init(id:UInt64, 
48            currentPrice: UFix64, 
49            bids:UInt64, 
50            active: Bool, 
51            artId: UInt64?,
52            leader:Address?, 
53            bidIncrement: UFix64,
54            owner: Address, 
55            startTime: Fix64,
56            endTime: Fix64,
57            completed: Bool,
58            expired:Bool
59        ) {
60            self.id=id
61            self.price= currentPrice
62            self.bids=bids
63            self.active=active
64            self.artId=artId
65            self.leader= leader
66            self.bidIncrement=bidIncrement
67            self.owner=owner
68            self.startTime=startTime
69            self.endTime=endTime
70            self.completed=completed
71            self.expired=expired
72        }
73    }
74
75    // AuctionItem contains the Resources for a single auction
76    pub resource AuctionItem {
77
78        //Number of bids made, that is aggregated to the status struct
79        priv var numberOfBids: UInt64
80        
81        // Resources
82        priv var NFT: @NonFungibleToken.NFT?
83        priv let bidVault: @FungibleToken.Vault
84
85        // Auction Settings
86        pub let auctionID: UInt64
87        priv let minimumBidIncrement: UFix64
88
89        // Auction State
90        access(account) var startPrice: UFix64
91        priv var currentPrice: UFix64
92
93        priv let auctionStartBlock: UInt64
94        priv var auctionCompleted: Bool
95
96        priv let endTime : Fix64
97        priv let startTime : Fix64
98    
99        priv let resale: Bool
100        priv let creator: Address?
101
102        // Recipient's Receiver Capabilities
103        priv var recipientCollectionCap: Capability<&{DisruptArt.DisruptArtCollectionPublic}>
104        priv var recipientVaultCap: Capability<&{FungibleToken.Receiver}>?
105
106        // Owner's Receiver Capabilities
107        priv let ownerCollectionCap: Capability<&{DisruptArt.DisruptArtCollectionPublic}>
108        priv let ownerVaultCap: Capability<&{FungibleToken.Receiver}>
109
110        init(
111            NFT: @NonFungibleToken.NFT,
112            bidVault: @FungibleToken.Vault,
113            auctionID: UInt64,
114            minimumBidIncrement: UFix64,
115            startPrice: UFix64,
116            auctionStartBlock: UInt64,
117            startTime : Fix64,
118            endTime : Fix64,
119            resale: Bool,
120            creator: Address?,
121            ownerCollectionCap: Capability<&{DisruptArt.DisruptArtCollectionPublic}>,
122            ownerVaultCap: Capability<&{FungibleToken.Receiver}>
123        ) {
124            self.NFT <- NFT
125            self.bidVault <- bidVault
126            self.auctionID = auctionID
127            self.minimumBidIncrement = minimumBidIncrement
128            self.startPrice = startPrice
129            self.currentPrice = startPrice
130            self.auctionStartBlock = auctionStartBlock
131            self.auctionCompleted = false
132            self.endTime = endTime
133            self.startTime = startTime
134            self.resale = resale
135            self.creator = creator
136            self.recipientCollectionCap = ownerCollectionCap
137            self.recipientVaultCap = ownerVaultCap
138            self.ownerCollectionCap = ownerCollectionCap
139            self.ownerVaultCap = ownerVaultCap
140            self.numberOfBids = 0   
141        }
142
143        // depositBidTokens deposits the bidder's tokens into the AuctionItem's Vault
144        pub fun depositBidTokens(vault: @FungibleToken.Vault) {
145            self.bidVault.deposit(from: <-vault)
146        }
147
148        // withdrawNFT removes the NFT from the AuctionItem and returns it to the caller
149        pub fun withdrawNFT(): @NonFungibleToken.NFT {
150            let NFT <- self.NFT <- nil
151            return <- NFT!
152        }
153        
154        // sendNFT sends the NFT to the Collection belonging to the provided Capability
155        access(contract) fun sendNFT(_ capability: Capability<&{DisruptArt.DisruptArtCollectionPublic}>) {
156            // borrow a reference to the owner's NFT receiver
157            if let collectionRef = capability.borrow() {
158                let NFT <- self.withdrawNFT()
159                // deposit the token into the owner's collection
160                collectionRef.deposit(token: <-NFT)
161            } else {
162                panic("sendNFT(): unable to borrow collection ref")
163            }
164        }
165
166        // sendBidTokens sends the bid tokens to the Vault Receiver belonging to the provided Capability
167        access(contract) fun sendBidTokens(_ capability: Capability<&{FungibleToken.Receiver}>, sale: Bool) {
168            // borrow a reference to the owner's NFT receiver
169            if let vaultRef = capability.borrow() {
170                let bidVaultRef = &self.bidVault as &FungibleToken.Vault
171                var balance = 0.0
172                if(sale){
173                    let marketShare = (bidVaultRef.balance / 100.0 ) * DisruptArtMarketplaceFlow.marketFee
174                    let royalityShare = (bidVaultRef.balance / 100.0 ) * DisruptArtMarketplaceFlow.royality
175                    balance = bidVaultRef.balance - (marketShare + royalityShare)
176                      
177                    let marketCut <- bidVaultRef.withdraw(amount: marketShare)
178                    let royalityCut <- bidVaultRef.withdraw(amount: royalityShare)
179
180                    let disruptartvaultRef =  getAccount(DisruptArtMarketplaceFlow.marketAddress)
181                                  .getCapability(/public/flowTokenReceiver)
182                                .borrow<&{FungibleToken.Receiver}>()
183                                ?? panic("failed to borrow reference to Marketplace vault")
184
185                    // let itemRef = &self.auctionItems[id] as? &AuctionItem
186
187                    let creatorvaultRef =  getAccount(self.creator!!)
188                                 .getCapability(/public/flowTokenReceiver)
189                                .borrow<&{FungibleToken.Receiver}>()
190                                ?? panic("failed to borrow reference to owner vault")
191
192                    disruptartvaultRef.deposit(from: <-marketCut)
193
194                    if(self.resale) {
195                       creatorvaultRef.deposit(from: <-royalityCut) 
196                    } else {
197                       disruptartvaultRef.deposit(from: <-royalityCut)
198                    }
199
200                } else {
201                    balance = bidVaultRef.balance
202                }              
203
204                vaultRef.deposit(from: <-bidVaultRef.withdraw(amount:balance))
205            } else {
206                panic("couldn't get vault ref")
207            }
208        }
209
210        // settleAuction sends the auction item to the highest bidder
211        // and deposits the FungibleTokens into the auction owner's account
212        pub fun settleAuction() {
213
214            pre {
215                !self.auctionCompleted : "The auction is already settled"
216                self.NFT != nil: "NFT in auction does not exist"
217                self.isAuctionExpired() : "Auction has not completed yet"
218            }
219               
220            // return if there are no bids to settle
221            if self.currentPrice == self.startPrice {
222                self.returnAuctionItemToOwner()
223            } else {            
224                self.exchangeTokens()
225            }
226
227            self.auctionCompleted = true
228            
229            emit AuctionSettled(auctionID: self.auctionID, price: self.currentPrice)
230
231        }
232
233        // isAuctionExpired returns true if the auction has exceeded it's length in blocks,
234        // otherwise it returns false
235        pub fun isAuctionExpired(): Bool {
236   
237            let currentTime = getCurrentBlock().timestamp
238            
239            if Fix64(self.endTime) < Fix64(currentTime) {
240                return true
241            } else {
242                return false
243            }
244        }
245
246        // returnAuctionItemToOwner releases any bids and returns the NFT
247        // to the owner's Collection
248        pub fun returnAuctionItemToOwner() {
249            pre {
250                self.NFT != nil: "NFT in auction does not exist"
251            }
252            
253            // release the bidder's tokens
254            self.releasePreviousBid()
255            
256            // deposit the NFT into the owner's collection
257            self.sendNFT(self.ownerCollectionCap)
258        }
259
260
261        // exchangeTokens sends the purchased NFT to the buyer and the bidTokens to the seller
262        pub fun exchangeTokens() {
263            pre {
264                self.NFT != nil: "NFT in auction does not exist"
265            }
266
267            self.sendNFT(self.recipientCollectionCap)
268            self.sendBidTokens(self.ownerVaultCap, sale:true)
269        }
270
271        // releasePreviousBid returns the outbid user's tokens to
272        // their vault receiver
273        pub fun releasePreviousBid() {
274            // release the bidTokens from the vault back to the bidder
275            if let vaultCap = self.recipientVaultCap {
276                self.sendBidTokens(self.recipientVaultCap!, sale:false)
277            } else {
278                panic("unable to get vault capability")
279            }
280        }
281
282         pub fun cancelAuction() {
283            pre {
284                !self.auctionCompleted : "The auction is already settled"
285                self.NFT != nil: "NFT in auction does not exist"
286                self.isAuctionExpired() == false : "Auciton expired, can't cancel"
287            }
288            
289            self.returnAuctionItemToOwner()
290          
291            self.auctionCompleted = true
292            
293            emit Canceled(auctionID: self.auctionID)
294          
295        }
296
297        pub fun placeBid(bidTokens: @FungibleToken.Vault, vaultCap: Capability<&{FungibleToken.Receiver}>, collectionCap: Capability<&{DisruptArt.DisruptArtCollectionPublic}>)  {
298            pre {
299                !self.auctionCompleted : "The auction is already settled"
300                self.NFT != nil: "NFT in auction does not exist"
301                !self.isAuctionExpired() : "Auciton expired, can't place a bid"
302            }
303           
304            if bidTokens.balance < (self.currentPrice + self.minimumBidIncrement) {
305                panic("bid amount be larger than minimum bid increment")
306            }
307            
308            if self.bidVault.balance != UFix64(0) {
309                if let vaultCapy = self.recipientVaultCap {
310                    self.sendBidTokens(vaultCapy, sale:false)
311                } else {
312                    panic("unable to get recipient Vault capability")
313                }
314            }
315
316            // Update the auction item
317            self.depositBidTokens(vault: <-bidTokens)
318
319            // Update the current price of the token
320            self.currentPrice = self.bidVault.balance
321
322            // Add the bidder's Vault and NFT receiver references
323            self.recipientCollectionCap = collectionCap
324            self.recipientVaultCap = vaultCap
325            self.numberOfBids=self.numberOfBids+(1 as UInt64)
326
327            emit NewBid(auctionID: self.auctionID, bidPrice: self.currentPrice, bidder: vaultCap.address)
328        }
329
330        pub fun getAuctionStatus() :AuctionStatus {
331
332            var leader:Address?= nil
333            if let recipient = self.recipientVaultCap {
334                leader=recipient.address
335            }
336
337            return AuctionStatus(
338                id:self.auctionID,
339                currentPrice: self.currentPrice, 
340                bids: self.numberOfBids,
341                active: !self.auctionCompleted  && !self.isAuctionExpired(),
342                artId: self.NFT?.id,
343                leader: leader,
344                bidIncrement: self.minimumBidIncrement,
345                owner: self.ownerVaultCap.address,
346                startTime: Fix64(self.startTime),
347                endTime: Fix64(self.endTime),
348                completed: self.auctionCompleted,
349                expired: self.isAuctionExpired()
350            )
351        }
352
353
354        destroy() {
355            // send the NFT back to auction owner
356            self.sendNFT(self.ownerCollectionCap)
357            
358            // if there's a bidder...
359            if let vaultCap = self.recipientVaultCap {
360                // ...send the bid tokens back to the bidder
361                self.sendBidTokens(vaultCap, sale:false)
362            }
363
364            destroy self.NFT
365            destroy self.bidVault
366        }
367    }
368
369    // AuctionPublic is a resource interface that restricts users to
370    // retreiving the auction price list and placing bids
371    pub resource interface AuctionPublic {
372        pub fun getAuctionKeys() : [UInt64]
373
374        pub fun getAuctionStatuses(): {UInt64: AuctionStatus}
375        pub fun getAuctionStatus(_ id:UInt64): AuctionStatus
376
377        pub fun placeBid(
378            id: UInt64, 
379            bidTokens: @FungibleToken.Vault, 
380            vaultCap: Capability<&{FungibleToken.Receiver}>, 
381            collectionCap: Capability<&{DisruptArt.DisruptArtCollectionPublic}>
382        )
383
384        pub fun settleAuction(_ id: UInt64)
385    }
386
387    // AuctionCollection contains a dictionary of AuctionItems and provides
388    // methods for manipulating the AuctionItems
389    pub resource AuctionCollection: AuctionPublic {
390
391        // Auction Items
392        access(account) var auctionItems: @{UInt64: AuctionItem}
393        
394        init() {
395            self.auctionItems <- {}
396        }
397
398        // addTokenToauctionItems adds an NFT to the auction items 
399        pub fun addTokenToAuctionItems(token: @NonFungibleToken.NFT, minimumBidIncrement: UFix64, startPrice: UFix64, bidVault: @FungibleToken.Vault, collectionCap: Capability<&{DisruptArt.DisruptArtCollectionPublic}>, vaultCap: Capability<&{FungibleToken.Receiver}>, endTime : Fix64) {
400            
401            pre {
402                Fix64(getCurrentBlock().timestamp) < endTime : "endtime should be greater than current time"
403                minimumBidIncrement > 0.0 : "minimumBidIncrement should be greater than 0.0"
404            }
405
406            let bidtoken <-token as! @DisruptArt.NFT
407            
408            let tokenID = bidtoken.id
409
410            let resale = (bidtoken.creator == self.owner?.address) ? false : true
411
412            let creator = bidtoken.creator 
413
414            let itemToken <- bidtoken as! @NonFungibleToken.NFT
415     
416            DisruptArtAuctionFlow.totalAuctions = DisruptArtAuctionFlow.totalAuctions + UInt64(1)
417
418            let id = DisruptArtAuctionFlow.totalAuctions
419
420            let startBlock = getCurrentBlock().height
421
422            let startTime = Fix64(getCurrentBlock().timestamp)
423
424            // create a new auction items resource container
425            let item <- create AuctionItem(
426                NFT: <-itemToken,
427                bidVault: <-bidVault,
428                auctionID: id,
429                minimumBidIncrement: minimumBidIncrement,
430                startPrice: startPrice,
431                auctionStartBlock: startBlock,
432                startTime : startTime,
433                endTime : endTime,
434                resale: resale,
435                creator: creator,
436                ownerCollectionCap: collectionCap,
437                ownerVaultCap: vaultCap
438            )
439
440            
441
442            // update the auction items dictionary with the new resources
443            let oldItem <- self.auctionItems[id] <- item
444            destroy oldItem
445
446            emit TokenAddedToAuctionItems(auctionID: id, startPrice: startPrice, minimumBidIncrement: minimumBidIncrement, auctionStartBlock: startBlock, tokenID:tokenID, endTime:endTime)
447        }
448
449        pub fun getAuctionStatuses(): {UInt64: AuctionStatus} {
450            pre {
451                self.auctionItems.keys.length > 0: "There are no auction items"
452            }
453
454            let auctionList: {UInt64: AuctionStatus} = {}
455
456            for id in self.auctionItems.keys {
457                let itemRef = (&self.auctionItems[id] as? &AuctionItem?)!
458                auctionList[id] = itemRef.getAuctionStatus()
459            }
460
461            return auctionList
462
463        }
464
465
466        pub fun getAuctionStatus(_ id:UInt64): AuctionStatus {
467            pre {
468                self.auctionItems[id] != nil:
469                    "Auction doesn't exist"
470            }
471
472            // Get the auction item resources
473            let itemRef = (&self.auctionItems[id] as &AuctionItem?)!
474            let status = itemRef.getAuctionStatus()
475            return status
476        }
477
478        pub fun getAuctionKeys() : [UInt64] {
479
480            pre {
481                self.auctionItems.keys.length > 0: "There are no auction items"
482            }
483
484            return self.auctionItems.keys
485
486        }
487
488        // settleAuction sends the auction item to the highest bidder
489        // and deposits the FungibleTokens into the auction owner's account
490        pub fun settleAuction(_ id: UInt64) {
491            pre {
492                self.auctionItems[id] != nil:
493                    "Auction doesn't exist"
494            }
495
496            let itemRef = (&self.auctionItems[id] as &AuctionItem?)!
497            itemRef.settleAuction()
498        }
499
500        pub fun cancelAuction(_ id: UInt64) {
501            pre {
502                self.auctionItems[id] != nil:
503                    "Auction does not exist"
504            }
505
506            let itemRef = (&self.auctionItems[id] as &AuctionItem?)!
507            itemRef.cancelAuction()
508          
509        }
510
511        // placeBid sends the bidder's tokens to the bid vault and updates the
512        // currentPrice of the current auction item
513        pub fun placeBid(id: UInt64, bidTokens: @FungibleToken.Vault, vaultCap: Capability<&{FungibleToken.Receiver}>, collectionCap: Capability<&{DisruptArt.DisruptArtCollectionPublic}>)  {
514           
515            pre {
516                self.auctionItems[id] != nil:
517                    "Auction doesn't exist"
518            }
519
520            // Get the auction item resources
521            let itemRef = (&self.auctionItems[id] as &AuctionItem?)!
522
523            itemRef.placeBid(bidTokens: <- bidTokens, vaultCap: vaultCap, collectionCap: collectionCap)
524
525        }
526
527        destroy() {
528            // destroy the empty resources
529            destroy self.auctionItems
530        }
531    }
532
533    // createAuctionCollection returns a new AuctionCollection resource to the caller
534    pub fun createAuctionCollection(): @AuctionCollection {
535        let auctionCollection <- create AuctionCollection()
536        return <- auctionCollection
537    }
538
539    init() {
540        self.totalAuctions = DisruptArtAuction.totalAuctions
541        self.auctionStoragePath= /storage/DisruptArtAuctionFlow
542        self.auctionPublicPath= /public/DisruptArtAuctionFlow
543    }   
544}
545 
546