Smart Contract

FindLeaseMarketAuctionSoft

A.097bafa4e0b48eef.FindLeaseMarketAuctionSoft

Deployed

2d ago
Feb 26, 2026, 03:12:51 AM UTC

Dependents

1 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import MetadataViews from 0x1d7e57aa55817448
4import FindViews from 0x097bafa4e0b48eef
5import Clock from 0x097bafa4e0b48eef
6import FIND from 0x097bafa4e0b48eef
7import FindMarket from 0x097bafa4e0b48eef
8import FindLeaseMarket from 0x097bafa4e0b48eef
9
10// An auction saleItem contract that escrows the FT, does _not_ escrow the NFT
11access(all) contract FindLeaseMarketAuctionSoft {
12
13    // A seller can list,delist and relist leases for auction
14    access(all) entitlement Seller
15
16    access(all) event EnglishAuction(tenant: String, id: UInt64, saleID: UInt64, seller: Address, sellerName:String?, amount: UFix64, auctionReservePrice: UFix64, status: String, vaultType:String, leaseInfo:FindLeaseMarket.LeaseInfo?, buyer:Address?, buyerName:String?, buyerAvatar:String?, endsAt: UFix64?, previousBuyer:Address?, previousBuyerName:String?)
17
18    access(all) resource SaleItem : FindLeaseMarket.SaleItem {
19        access(contract) var pointer: FindLeaseMarket.AuthLeasePointer
20        access(contract) var vaultType: Type
21        access(contract) var auctionStartPrice: UFix64
22        access(contract) var auctionReservePrice: UFix64
23        access(contract) var auctionDuration: UFix64
24        access(contract) var auctionMinBidIncrement: UFix64
25        access(contract) var auctionExtensionOnLateBid: UFix64
26        access(contract) var auctionStartedAt: UFix64?
27        access(contract) var auctionValidUntil: UFix64?
28        access(contract) var auctionEndsAt: UFix64?
29        access(contract) var offerCallback: Capability<&MarketBidCollection>?
30        access(contract) var saleItemExtraField: {String : AnyStruct}
31
32        init(pointer: FindLeaseMarket.AuthLeasePointer, vaultType: Type, auctionStartPrice:UFix64, auctionReservePrice:UFix64, auctionValidUntil: UFix64?, saleItemExtraField: {String : AnyStruct}) {
33            self.vaultType=vaultType
34            self.pointer=pointer
35            self.auctionStartPrice=auctionStartPrice
36            self.auctionReservePrice=auctionReservePrice
37            self.auctionDuration=86400.0
38            self.auctionExtensionOnLateBid=300.0
39            self.auctionMinBidIncrement=10.0
40            self.offerCallback=nil
41            self.auctionStartedAt=nil
42            self.auctionValidUntil=auctionValidUntil
43            self.auctionEndsAt=nil
44            self.saleItemExtraField=saleItemExtraField
45        }
46
47        //Here we do not get a vault back, it is sent in to the method itself
48        access(contract) fun acceptNonEscrowedBid() {
49            pre{
50                self.offerCallback != nil : "There is no bid offer to the item."
51                self.offerCallback!.check() : "Bidder unlinked bid collection capability."
52            }
53            self.offerCallback!.borrow()!.accept(self.getLeaseName())
54            self.pointer.move(to: self.offerCallback!.address)
55        }
56
57        access(all) fun getBalance() : UFix64 {
58            if let cb= self.offerCallback {
59                return cb.borrow()?.getBalance(self.getLeaseName()) ?? panic("Bidder unlinked bid collection capability. bidder address : ".concat(cb.address.toString()))
60            }
61            return self.auctionStartPrice
62        }
63
64        access(all) fun getSeller() : Address {
65            return self.pointer.owner()
66        }
67
68        access(all) fun getSellerName() : String? {
69            let address = self.pointer.owner()
70            return FIND.reverseLookup(address)
71        }
72
73        access(all) fun getBuyer() : Address? {
74            if let cb= self.offerCallback {
75                return cb.address
76            }
77            return nil
78        }
79
80        access(all) fun getId() : UInt64{
81            return self.pointer.getUUID()
82        }
83
84        access(all) fun getBuyerName() : String? {
85            if let cb= self.offerCallback {
86                return FIND.reverseLookup(cb.address)
87            }
88            return nil
89        }
90
91        access(all) fun toLeaseInfo() : FindLeaseMarket.LeaseInfo {
92            return FindLeaseMarket.LeaseInfo(self.pointer)
93        }
94
95        access(contract) fun setAuctionStarted(_ startedAt: UFix64) {
96            self.auctionStartedAt=startedAt
97        }
98
99        access(contract) fun setAuctionEnds(_ endsAt: UFix64){
100            self.auctionEndsAt=endsAt
101        }
102
103        access(all) fun hasAuctionStarted() : Bool {
104            if let starts = self.auctionStartedAt {
105                return starts <= Clock.time()
106            }
107            return false
108        }
109
110        access(all) fun hasAuctionEnded() : Bool {
111            if let ends = self.auctionEndsAt {
112                return ends < Clock.time()
113            }
114            panic("Not a live auction")
115        }
116
117        access(all) fun hasAuctionMetReservePrice() : Bool {
118
119            let balance=self.getBalance()
120
121            if self.auctionReservePrice== nil {
122                return false
123            }
124
125            return balance >= self.auctionReservePrice
126        }
127
128        access(contract) fun setExtentionOnLateBid(_ time: UFix64) {
129            self.auctionExtensionOnLateBid=time
130        }
131
132        access(contract) fun setAuctionDuration(_ duration: UFix64) {
133            self.auctionDuration=duration
134        }
135
136        access(contract) fun setReservePrice(_ price: UFix64) {
137            self.auctionReservePrice=price
138        }
139
140        access(contract) fun setMinBidIncrement(_ price: UFix64) {
141            self.auctionMinBidIncrement=price
142        }
143
144        access(contract) fun setStartAuctionPrice(_ price: UFix64) {
145            self.auctionStartPrice=price
146        }
147
148        access(contract) fun setCallback(_ callback: Capability<&MarketBidCollection>?) {
149            self.offerCallback=callback
150        }
151
152        access(all) fun getSaleType(): String {
153            if self.auctionStartedAt != nil {
154                if self.hasAuctionEnded() {
155                    if self.hasAuctionMetReservePrice() {
156                        return "finished_completed"
157                    }
158                    return "finished_failed"
159                }
160                return "active_ongoing"
161            }
162            return "active_listed"
163        }
164
165        access(all) fun getListingType() : Type {
166            return Type<@SaleItem>()
167        }
168
169        access(all) fun getListingTypeIdentifier() : String {
170            return Type<@SaleItem>().identifier
171        }
172
173        access(all) fun getLeaseName() : String {
174            return self.pointer.name
175        }
176
177        access(all) fun getItemType() : Type {
178            return Type<@FIND.Lease>()
179        }
180
181        access(all) fun getAuction(): FindLeaseMarket.AuctionItem? {
182            return FindLeaseMarket.AuctionItem(startPrice: self.auctionStartPrice,
183            currentPrice: self.getBalance(),
184            minimumBidIncrement: self.auctionMinBidIncrement ,
185            reservePrice: self.auctionReservePrice,
186            extentionOnLateBid: self.auctionExtensionOnLateBid ,
187            auctionEndsAt: self.auctionEndsAt ,
188            timestamp: Clock.time())
189        }
190
191        access(all) fun getFtType() : Type {
192            return self.vaultType
193        }
194
195        access(contract) fun setValidUntil(_ time: UFix64?) {
196            self.auctionValidUntil=time
197        }
198
199        access(all) fun getValidUntil() : UFix64? {
200            if self.hasAuctionStarted() {
201                return self.auctionEndsAt
202            }
203            return self.auctionValidUntil
204        }
205
206        access(all) fun checkPointer() : Bool {
207            return self.pointer.valid()
208        }
209
210        access(all) fun getSaleItemExtraField() : {String : AnyStruct} {
211            return self.saleItemExtraField
212        }
213    }
214
215    access(all) resource interface SaleItemCollectionPublic {
216        //fetch all the tokens in the collection
217        access(all) fun getNameSales(): [String]
218        access(all) fun containsNameSale(_ name: String): Bool
219        access(contract) fun registerIncreasedBid(_ name: String, oldBalance: UFix64)
220
221        //place a bid on a token
222        access(contract) fun registerBid(name: String, callback: Capability<&MarketBidCollection>, vaultType:Type)
223
224        //only buyer can fulfill auctions since he needs to send funds for this type
225        access(contract) fun fulfillAuction(name: String, vault: @{FungibleToken.Vault})
226    }
227
228    access(all) resource SaleItemCollection: SaleItemCollectionPublic, FindLeaseMarket.SaleItemCollectionPublic  {
229        //is this the best approach now or just put the NFT inside the saleItem?
230        access(contract) var items: @{String: SaleItem}
231
232        access(contract) let tenantCapability: Capability<&FindMarket.Tenant>
233
234        init (_ tenantCapability: Capability<&FindMarket.Tenant>) {
235            self.items <- {}
236            self.tenantCapability=tenantCapability
237        }
238
239        access(self) fun getTenant() : &FindMarket.Tenant {
240            pre{
241                self.tenantCapability.check() : "Tenant client is not linked anymore"
242            }
243            return self.tenantCapability.borrow()!
244        }
245
246        access(self) fun emitEvent(saleItem: &SaleItem, status: String,previousBuyer:Address?) {
247            let owner=saleItem.getSeller()
248            let ftType=saleItem.getFtType()
249            let balance=saleItem.getBalance()
250            let seller=saleItem.getSeller()
251            let name=saleItem.getLeaseName()
252            let buyer=saleItem.getBuyer()
253
254            var leaseInfo:FindLeaseMarket.LeaseInfo?=nil
255            if saleItem.checkPointer() {
256                leaseInfo=saleItem.toLeaseInfo()
257            }
258
259            var previousBuyerName : String?=nil
260            if let pb= previousBuyer {
261                previousBuyerName = FIND.reverseLookup(pb)
262            }
263
264            if buyer != nil {
265                let buyerName=FIND.reverseLookup(buyer!)
266                let profile = FIND.lookup(buyer!.toString())
267                emit EnglishAuction(tenant:self.getTenant().name, id: saleItem.getId(), saleID: saleItem.uuid, seller:seller, sellerName: FIND.reverseLookup(seller), amount: balance, auctionReservePrice: saleItem.auctionReservePrice,  status: status, vaultType:saleItem.vaultType.identifier, leaseInfo: leaseInfo,  buyer: buyer, buyerName: buyerName, buyerAvatar: profile?.getAvatar(), endsAt: saleItem.auctionEndsAt, previousBuyer:previousBuyer, previousBuyerName:previousBuyerName)
268            } else {
269                emit EnglishAuction(tenant:self.getTenant().name, id: saleItem.getId(), saleID: saleItem.uuid, seller:seller, sellerName: FIND.reverseLookup(seller), amount: balance, auctionReservePrice: saleItem.auctionReservePrice,  status: status, vaultType:saleItem.vaultType.identifier, leaseInfo: leaseInfo,  buyer: nil, buyerName: nil, buyerAvatar: nil, endsAt: saleItem.auctionEndsAt, previousBuyer:previousBuyer, previousBuyerName:previousBuyerName)
270            }
271        }
272
273        access(all) fun getListingType() : Type {
274            return Type<@SaleItem>()
275        }
276
277        access(self) fun addBid(name:String, newOffer: Capability<&MarketBidCollection>, oldBalance: UFix64) {
278            let saleItem=self.borrowAuth(name)
279
280            let actionResult=self.getTenant().allowedAction(listingType: self.getListingType(), nftType: saleItem.getItemType(), ftType: saleItem.getFtType(), action: FindMarket.MarketAction(listing:false, name:"add bit in soft-auction"), seller: self.owner!.address ,buyer: newOffer.address)
281
282            if !actionResult.allowed {
283                panic(actionResult.message)
284            }
285
286            let timestamp=Clock.time()
287            let newOfferBalance=newOffer.borrow()?.getBalance(name) ?? panic("The new offer bid capability is invalid.")
288
289            let previousOffer = saleItem.offerCallback!
290
291
292            var minBid=oldBalance + saleItem.auctionMinBidIncrement
293            if newOffer.address != previousOffer.address {
294                let previousBalance = previousOffer.borrow()?.getBalance(name) ?? panic("Previous bidder unlinked the bid ccollection capability. bidder address : ".concat(previousOffer.address.toString()))
295                minBid = previousBalance + saleItem.auctionMinBidIncrement
296            }
297
298            if newOfferBalance < minBid {
299                panic("bid ".concat(newOfferBalance.toString()).concat(" must be larger then previous bid+bidIncrement ").concat(minBid.toString()))
300            }
301
302            var previousBuyer:Address?=nil
303            if newOffer.address != previousOffer.address {
304                previousOffer.borrow()!.cancelBidFromSaleItem(name)
305                previousBuyer=previousOffer.address
306            }
307
308            saleItem.setCallback(newOffer)
309
310            let suggestedEndTime=timestamp+saleItem.auctionExtensionOnLateBid
311
312            if suggestedEndTime > saleItem.auctionEndsAt! {
313                saleItem.setAuctionEnds(suggestedEndTime)
314            }
315            self.emitEvent(saleItem: saleItem, status: "active_ongoing", previousBuyer:previousBuyer)
316
317        }
318
319        access(contract) fun registerIncreasedBid(_ name: String, oldBalance:UFix64) {
320            pre {
321                self.items.containsKey(name) : "Invalid lease name=".concat(name)
322            }
323
324            let saleItem=self.borrow(name)
325
326            if !saleItem.hasAuctionStarted()  {
327                panic("Auction is not started")
328            }
329
330            if saleItem.hasAuctionEnded() {
331                panic("Auction has ended")
332            }
333
334            self.addBid(name: name, newOffer: saleItem.offerCallback!, oldBalance: oldBalance)
335
336        }
337
338        //This is a function that buyer will call (via his bid collection) to register the bicCallback with the seller
339        access(contract) fun registerBid(name: String, callback: Capability<&MarketBidCollection>, vaultType: Type) {
340
341            let timestamp=Clock.time()
342
343            let name = name
344
345            let saleItem=self.borrowAuth(name)
346            if saleItem.hasAuctionStarted() {
347                if saleItem.hasAuctionEnded() {
348                    panic("Auction has ended")
349                }
350
351                if let cb = saleItem.offerCallback {
352                    if cb.address == callback.address {
353                        panic("You already have the latest bid on this item, use the incraseBid transaction")
354                    }
355                }
356
357                self.addBid(name: name, newOffer: callback, oldBalance: 0.0)
358                return
359            }
360
361            let actionResult=self.getTenant().allowedAction(listingType: self.getListingType(), nftType: saleItem.getItemType(), ftType: saleItem.getFtType(), action: FindMarket.MarketAction(listing:false, name:"bid item in soft-auction"), seller: self.owner!.address, buyer: callback.address)
362
363            if !actionResult.allowed {
364                panic(actionResult.message)
365            }
366
367            let balance=callback.borrow()?.getBalance(name) ?? panic("Bidder unlinked the bid collection capability. bidder address : ".concat(callback.address.toString()))
368
369            if saleItem.auctionStartPrice >  balance {
370                panic("You need to bid more then the starting price of ".concat(saleItem.auctionStartPrice.toString()))
371            }
372
373            if let valid = saleItem.getValidUntil() {
374                assert( valid >= Clock.time(), message: "This auction listing is already expired")
375            }
376
377            saleItem.setCallback(callback)
378            let duration=saleItem.auctionDuration
379            let endsAt=timestamp + duration
380            saleItem.setAuctionStarted(timestamp)
381            saleItem.setAuctionEnds(endsAt)
382
383            self.emitEvent(saleItem: saleItem, status: "active_ongoing", previousBuyer:nil)
384        }
385
386        access(Seller) fun cancel(_ name: String) {
387            pre {
388                self.items.containsKey(name) : "Invalid lease name=".concat(name)
389            }
390
391            let saleItem=self.borrow(name)
392
393            var status="cancel"
394            if saleItem.checkPointer() {
395                if saleItem.hasAuctionStarted() && saleItem.hasAuctionEnded() {
396                    if saleItem.hasAuctionMetReservePrice() {
397                        panic("Cannot cancel finished auction, fulfill it instead")
398                    }
399                    status="cancel_reserved_not_met"
400
401                }
402            } else {
403                status="cancel_ghostlisting"
404            }
405            let actionResult=self.getTenant().allowedAction(listingType: self.getListingType(), nftType: saleItem.getItemType(), ftType: saleItem.getFtType(), action: FindMarket.MarketAction(listing:false, name:"delist item from soft-auction"), seller: nil, buyer: nil)
406
407            if !actionResult.allowed {
408                panic(actionResult.message)
409            }
410
411            self.emitEvent(saleItem: saleItem, status: status, previousBuyer:nil)
412
413            if saleItem.offerCallback != nil && saleItem.offerCallback!.check() {
414                saleItem.offerCallback!.borrow()!.cancelBidFromSaleItem(name)
415            }
416
417            destroy <- self.items.remove(key: name)
418        }
419
420        access(contract) fun fulfillAuction(name: String, vault: @{FungibleToken.Vault}) {
421            pre {
422                self.items.containsKey(name) : "Invalid lease name=".concat(name)
423            }
424
425            let saleItem = self.borrowAuth(name)
426
427            if !saleItem.hasAuctionStarted() {
428                panic("This auction is not live")
429            }
430
431            if !saleItem.hasAuctionEnded() {
432                panic("Auction has not ended yet")
433            }
434
435            if vault.getType() != saleItem.vaultType {
436                panic("The FT vault sent in to fulfill does not match the required type. Required Type : ".concat(saleItem.vaultType.identifier).concat(" . Sent-in vault type : ".concat(vault.getType().identifier)))
437            }
438
439            if vault.balance < saleItem.auctionReservePrice {
440                panic("cannot fulfill auction reserve price was not met, cancel it without a vault ".concat(vault.balance.toString()).concat(" < ").concat(saleItem.auctionReservePrice.toString()))
441            }
442
443            let actionResult=self.getTenant().allowedAction(listingType: self.getListingType(), nftType: saleItem.getItemType(), ftType: saleItem.getFtType(), action: FindMarket.MarketAction(listing:false, name:"buy item for soft-auction"), seller: self.owner!.address,buyer: saleItem.offerCallback!.address)
444
445            if !actionResult.allowed {
446                panic(actionResult.message)
447            }
448
449            let cuts= self.getTenant().getCuts(name: actionResult.name, listingType: self.getListingType(), nftType: saleItem.getItemType(), ftType: saleItem.getFtType())
450
451
452            let leaseInfo=saleItem.toLeaseInfo()
453
454            self.emitEvent(saleItem: saleItem, status: "sold", previousBuyer:nil)
455            saleItem.acceptNonEscrowedBid()
456
457            FindLeaseMarket.pay(tenant:self.getTenant().name, leaseName:name, saleItem: saleItem, vault: <- vault, leaseInfo:leaseInfo, cuts:cuts)
458
459            destroy <- self.items.remove(key: name)
460
461        }
462
463        access(Seller) fun listForAuction(pointer: FindLeaseMarket.AuthLeasePointer, vaultType: Type, auctionStartPrice: UFix64, auctionReservePrice: UFix64, auctionDuration: UFix64, auctionExtensionOnLateBid: UFix64, minimumBidIncrement: UFix64, auctionValidUntil: UFix64?, saleItemExtraField: {String : AnyStruct}) {
464
465            // ensure it is not a 0 dollar listing
466            if auctionStartPrice <= 0.0 {
467                panic("Auction start price should be greater than 0")
468            }
469
470            // ensure it is not a 0 dollar listing
471            if auctionReservePrice < auctionStartPrice {
472                panic("Auction reserve price should be greater than Auction start price")
473            }
474
475            // ensure validUntil is valid
476            if auctionValidUntil != nil && auctionValidUntil! < Clock.time() {
477                panic("Valid until is before current time")
478            }
479
480            let saleItem <- create SaleItem(pointer: pointer, vaultType:vaultType, auctionStartPrice: auctionStartPrice, auctionReservePrice:auctionReservePrice, auctionValidUntil: auctionValidUntil, saleItemExtraField: saleItemExtraField)
481
482            let actionResult=self.getTenant().allowedAction(listingType: self.getListingType(), nftType: saleItem.getItemType(), ftType: saleItem.getFtType(), action: FindMarket.MarketAction(listing:true, name:"list item for soft-auction"), seller: self.owner!.address, buyer: nil)
483
484            if !actionResult.allowed {
485                panic(actionResult.message)
486            }
487
488            assert(self.items[pointer.name] == nil , message: "Auction listing for this item is already created.")
489
490            saleItem.setAuctionDuration(auctionDuration)
491            saleItem.setExtentionOnLateBid(auctionExtensionOnLateBid)
492            saleItem.setMinBidIncrement(minimumBidIncrement)
493            self.items[pointer.name] <-! saleItem
494            let saleItemRef = self.borrow(pointer.name)
495            self.emitEvent(saleItem: saleItemRef, status: "active_listed", previousBuyer:nil)
496        }
497
498        access(all) fun getNameSales(): [String] {
499            return self.items.keys
500        }
501
502        access(all) fun containsNameSale(_ name: String): Bool {
503            return self.items.containsKey(name)
504        }
505
506        access(all) fun borrow(_ name: String): &SaleItem {
507            pre{
508                self.items.containsKey(name) : "This name sale does not exist.".concat(name)
509            }
510            return (&self.items[name])!
511        }
512
513        access(Seller) fun borrowAuth(_ name: String): auth(Seller) &SaleItem {
514            pre{
515                self.items.containsKey(name) : "This name sale does not exist.".concat(name)
516            }
517            return (&self.items[name])!
518        }
519
520        access(all) fun borrowSaleItem(_ name: String) : &{FindLeaseMarket.SaleItem} {
521            pre{
522                self.items.containsKey(name) : "This name sale does not exist.".concat(name)
523            }
524            return (&self.items[name])!
525        }
526
527    }
528
529    access(all) resource Bid : FindLeaseMarket.Bid {
530        access(contract) let from: Capability<&SaleItemCollection>
531        access(contract) let leaseName: String
532
533        access(contract) let vaultType: Type
534        access(contract) var bidAt: UFix64
535        access(contract) var balance: UFix64
536        access(contract) let bidExtraField: {String : AnyStruct}
537
538        init(from: Capability<&SaleItemCollection>, leaseName: String, vaultType:Type,  balance:UFix64, bidExtraField: {String : AnyStruct}){
539            self.vaultType= vaultType
540            self.balance=balance
541            self.leaseName=leaseName
542            self.from=from
543            self.bidAt=Clock.time()
544            self.bidExtraField=bidExtraField
545        }
546
547        access(contract) fun setBalance(_ balance:UFix64) {
548            self.balance=balance
549        }
550
551        access(contract) fun setBidAt(_ time: UFix64) {
552            self.bidAt=time
553        }
554
555        access(all) fun getBalance() : UFix64 {
556            return self.balance
557        }
558
559        access(all) fun getSellerAddress() : Address {
560            return self.from.address
561        }
562
563        access(all) fun getBidExtraField() : {String : AnyStruct} {
564            return self.bidExtraField
565        }
566    }
567
568    access(all) resource interface MarketBidCollectionPublic {
569        access(all) fun getBalance(_ name: String) : UFix64
570        access(all) fun containsNameBid(_ name: String): Bool
571        access(contract) fun accept(_ name: String)
572        access(contract) fun cancelBidFromSaleItem(_ name: String)
573    }
574
575    // A Buyer can bid, increase bid and fulfill auctions
576    access(all) entitlement Buyer
577
578    //A collection stored for bidders/buyers
579    access(all) resource MarketBidCollection: MarketBidCollectionPublic, FindLeaseMarket.MarketBidCollectionPublic {
580        access(contract) var bids : @{String: Bid}
581        access(contract) let receiver: Capability<&{FungibleToken.Receiver}>
582        access(contract) let tenantCapability: Capability<&FindMarket.Tenant>
583
584        //not sure we can store this here anymore. think it needs to be in every bid
585        init(receiver: Capability<&{FungibleToken.Receiver}>, tenantCapability: Capability<&FindMarket.Tenant>) {
586            self.bids <- {}
587            self.receiver=receiver
588            self.tenantCapability=tenantCapability
589        }
590
591        access(self) fun getTenant() : &FindMarket.Tenant {
592            pre{
593                self.tenantCapability.check() : "Tenant client is not linked anymore"
594            }
595            return self.tenantCapability.borrow()!
596        }
597
598        //called from lease when auction is ended
599        access(contract) fun accept(_ name: String) {
600            pre {
601                self.bids[name] != nil : "You need to have a bid here already"
602            }
603
604            let bid <- self.bids.remove(key: name) ?? panic("missing bid")
605            destroy bid
606        }
607
608        access(all) fun getNameBids() : [String] {
609            return self.bids.keys
610        }
611
612        access(all) fun containsNameBid(_ name: String) : Bool {
613            return self.bids.containsKey(name)
614        }
615
616        access(all) fun getBidType() : Type {
617            return Type<@Bid>()
618        }
619
620        access(Buyer) fun bid(name: String, amount:UFix64, vaultType:Type, bidExtraField: {String : AnyStruct}) {
621            if self.owner!.address == FIND.status(name).owner! {
622                panic("You cannot bid on your own resource")
623            }
624
625            if self.bids[name] !=nil{
626                panic("You already have an bid for this item, use increaseBid on that bid")
627            }
628
629            let from=getAccount(FIND.status(name).owner!).capabilities.get<&SaleItemCollection>(self.getTenant().getPublicPath(Type<@SaleItemCollection>()))
630
631            let bid <- create Bid(from: from, leaseName:name, vaultType: vaultType, balance:amount, bidExtraField: bidExtraField)
632            let saleItemCollection= from.borrow() ?? panic("Could not borrow sale item for lease name=".concat(name))
633
634            let callbackCapability =self.owner!.capabilities.get<&MarketBidCollection>(self.getTenant().getPublicPath(Type<@MarketBidCollection>()))
635            let oldToken <- self.bids[name] <- bid
636            saleItemCollection.registerBid(name: name, callback: callbackCapability, vaultType: vaultType)
637            destroy oldToken
638        }
639
640        access(Buyer) fun fulfillAuction(name:String, vault: @{FungibleToken.Vault}) {
641            pre {
642                self.bids[name] != nil : "You need to have a bid here already"
643            }
644            let bid =self.borrowBid(name)
645            let saleItem=bid.from.borrow()!
646            saleItem.fulfillAuction(name:name, vault: <- vault)
647        }
648
649        //increase a bid, will not work if the auction has already started
650        access(Buyer) fun increaseBid(name: String, increaseBy: UFix64) {
651            pre {
652                self.bids[name] != nil : "You need to have a bid here already"
653            }
654            let bid =self.borrowBid(name)
655
656            let oldBalance=bid.balance
657
658            bid.setBidAt(Clock.time())
659            bid.setBalance(bid.balance + increaseBy)
660
661            if !bid.from.check(){
662                panic("Seller unlinked the SaleItem collection capability. seller address : ".concat(bid.from.address.toString()))
663            }
664            bid.from.borrow()!.registerIncreasedBid(name, oldBalance: oldBalance)
665        }
666
667        //called from saleItem when things are cancelled
668        //if the bid is canceled from seller then we move the vault tokens back into your vault
669        access(contract) fun cancelBidFromSaleItem(_ name: String) {
670            let bid <- self.bids.remove(key: name) ?? panic("missing bid")
671            destroy bid
672        }
673
674        access(all) fun borrowBid(_ name: String): &Bid {
675            pre{
676                self.bids.containsKey(name) : "This name lease bid does not exist.".concat(name)
677            }
678            return (&self.bids[name])!
679        }
680
681        access(all) fun borrowBidItem(_ name: String): &{FindLeaseMarket.Bid} {
682            pre{
683                self.bids.containsKey(name) : "This name lease bid does not exist.".concat(name)
684            }
685            return (&self.bids[name])!
686        }
687
688        access(all) fun getBalance(_ name: String) : UFix64 {
689            pre {
690                self.bids[name] != nil : "You need to have a bid here already"
691            }
692            let bid= self.borrowBid(name)
693            return bid.balance
694        }
695    }
696
697    //Create an empty lease collection that store your leases to a name
698    access(all) fun createEmptySaleItemCollection(_ tenantCapability: Capability<&FindMarket.Tenant>) : @SaleItemCollection {
699        return <- create SaleItemCollection(tenantCapability)
700    }
701
702    access(all) fun createEmptyMarketBidCollection(receiver: Capability<&{FungibleToken.Receiver}>, tenantCapability: Capability<&FindMarket.Tenant>) : @MarketBidCollection {
703        return <- create MarketBidCollection(receiver: receiver, tenantCapability:tenantCapability)
704    }
705
706    access(all) fun getSaleItemCapability(marketplace:Address, user:Address) : Capability<&{SaleItemCollectionPublic, FindLeaseMarket.SaleItemCollectionPublic}>? {
707        if FindMarket.getTenantCapability(marketplace) == nil {
708            panic("invalid tenant")
709        }
710        if let tenant=FindMarket.getTenantCapability(marketplace)!.borrow() {
711            return getAccount(user).capabilities.get<&{SaleItemCollectionPublic, FindLeaseMarket.SaleItemCollectionPublic}>(tenant.getPublicPath(Type<@SaleItemCollection>()))
712        }
713        return nil
714    }
715
716    access(all) fun getBidCapability( marketplace:Address, user:Address) : Capability<&{MarketBidCollectionPublic, FindLeaseMarket.MarketBidCollectionPublic}>? {
717        if FindMarket.getTenantCapability(marketplace) == nil {
718            panic("invalid tenant")
719        }
720        if let tenant=FindMarket.getTenantCapability(marketplace)!.borrow() {
721            return getAccount(user).capabilities.get<&{MarketBidCollectionPublic, FindLeaseMarket.MarketBidCollectionPublic}>(tenant.getPublicPath(Type<@MarketBidCollection>()))
722        }
723        return nil
724    }
725
726    init() {
727        FindLeaseMarket.addSaleItemType(Type<@SaleItem>())
728        FindLeaseMarket.addSaleItemCollectionType(Type<@SaleItemCollection>())
729        FindLeaseMarket.addMarketBidType(Type<@Bid>())
730        FindLeaseMarket.addMarketBidCollectionType(Type<@MarketBidCollection>())
731    }
732}
733