Smart Contract

DisruptArtAuction

A.cd946ef9b13804c6.DisruptArtAuction

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