Smart Contract
FindLeaseMarketAuctionSoft
A.097bafa4e0b48eef.FindLeaseMarketAuctionSoft
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