Smart Contract
FindMarketDirectOfferEscrow
A.097bafa4e0b48eef.FindMarketDirectOfferEscrow
1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import MetadataViews from 0x1d7e57aa55817448
4import FindViews from 0x097bafa4e0b48eef
5import Clock from 0x097bafa4e0b48eef
6import Debug from 0x097bafa4e0b48eef
7import FIND from 0x097bafa4e0b48eef
8import FindMarket from 0x097bafa4e0b48eef
9import Profile from 0x097bafa4e0b48eef
10
11access(all) contract FindMarketDirectOfferEscrow {
12
13 access(all) event DirectOffer(tenant: String, id: UInt64, saleID: UInt64, seller: Address, sellerName: String?, amount: UFix64, status: String, vaultType:String, nft: FindMarket.NFTInfo?, buyer:Address?, buyerName:String?, buyerAvatar: String?, endsAt: UFix64?, previousBuyer:Address?, previousBuyerName:String?)
14
15 access(all) entitlement Seller
16
17 access(all) resource SaleItem : FindMarket.SaleItem {
18
19 access(contract) var pointer: {FindViews.Pointer}
20
21 access(contract) var offerCallback: Capability<&MarketBidCollection>
22 access(contract) var validUntil: UFix64?
23 access(contract) var saleItemExtraField: {String : AnyStruct}
24 access(contract) let totalRoyalties: UFix64
25
26 init(pointer: {FindViews.Pointer}, callback: Capability<&MarketBidCollection>, validUntil: UFix64?, saleItemExtraField: {String : AnyStruct}) {
27 self.pointer=pointer
28 self.offerCallback=callback
29 self.validUntil=validUntil
30 self.saleItemExtraField=saleItemExtraField
31 self.totalRoyalties=self.pointer.getTotalRoyaltiesCut()
32 }
33
34 access(all) fun getId() : UInt64{
35 return self.pointer.getUUID()
36 }
37
38 access(contract) fun acceptEscrowedBid() : @{FungibleToken.Vault} {
39 if !self.offerCallback.check() {
40 panic("Bidder unlinked bid collection capability. Bidder Address : ".concat(self.offerCallback.address.toString()))
41 }
42 let pointer= self.pointer as! FindViews.AuthNFTPointer
43 let publicPath = pointer.getNFTCollectionData().publicPath
44 let vault <- self.offerCallback.borrow()!.accept(<- pointer.withdraw(), path:publicPath)
45 return <- vault
46 }
47
48 access(all) fun getRoyalty() : MetadataViews.Royalties {
49 return self.pointer.getRoyalty()
50 }
51
52 access(all) fun getBalance() : UFix64 {
53 if !self.offerCallback.check() {
54 panic("Bidder unlinked bid collection capability. Bidder Address : ".concat(self.offerCallback.address.toString()))
55 }
56 return self.offerCallback.borrow()!.getBalance(self.getId())
57 }
58
59 access(all) fun getSeller() : Address {
60 return self.pointer.owner()
61 }
62
63 access(all) fun getSellerName() : String? {
64 let address = self.pointer.owner()
65 return FIND.reverseLookup(address)
66 }
67
68
69 access(all) fun getBuyer() : Address? {
70 return self.offerCallback.address
71 }
72
73 access(all) fun getBuyerName() : String? {
74 if let name = FIND.reverseLookup(self.offerCallback.address) {
75 return name
76 }
77 return nil
78 }
79
80 access(all) fun toNFTInfo(_ detail: Bool) : FindMarket.NFTInfo{
81 return FindMarket.NFTInfo(self.pointer.getViewResolver(), id: self.pointer.id, detail:detail)
82 }
83
84 access(all) fun getSaleType() : String {
85 return "active_ongoing"
86 }
87
88 access(all) fun getListingType() : Type {
89 return Type<@SaleItem>()
90 }
91
92 access(all) fun getListingTypeIdentifier() : String {
93 return Type<@SaleItem>().identifier
94 }
95
96 access(contract) fun setPointer(_ pointer: FindViews.AuthNFTPointer) {
97 self.pointer=pointer
98 }
99
100 access(all) fun getItemID() : UInt64 {
101 return self.pointer.id
102 }
103
104 access(all) fun getItemType() : Type {
105 return self.pointer.getItemType()
106 }
107
108 access(all) fun getAuction(): FindMarket.AuctionItem? {
109 return nil
110 }
111
112 access(all) fun getFtType() : Type {
113 if !self.offerCallback.check() {
114 panic("Bidder unlinked bid collection capability. Bidder Address : ".concat(self.offerCallback.address.toString()))
115 }
116 return self.offerCallback.borrow()!.getVaultType(self.getId())
117 }
118
119 access(contract) fun setValidUntil(_ time: UFix64?) {
120 self.validUntil=time
121 }
122
123 access(all) fun getValidUntil() : UFix64? {
124 return self.validUntil
125 }
126
127 access(contract) fun setCallback(_ callback: Capability<&MarketBidCollection>) {
128 self.offerCallback=callback
129 }
130
131 access(all) fun checkPointer() : Bool {
132 return self.pointer.valid()
133 }
134
135 access(all) fun checkSoulBound() : Bool {
136 return self.pointer.checkSoulBound()
137 }
138
139 access(all) fun getSaleItemExtraField() : {String : AnyStruct} {
140 return self.saleItemExtraField
141 }
142
143 access(contract) fun setSaleItemExtraField(_ field: {String : AnyStruct}) {
144 self.saleItemExtraField = field
145 }
146
147 access(all) fun getTotalRoyalties() : UFix64 {
148 return self.totalRoyalties
149 }
150
151 access(all) fun validateRoyalties() : Bool {
152 return self.totalRoyalties == self.pointer.getTotalRoyaltiesCut()
153 }
154
155 access(all) fun getDisplay() : MetadataViews.Display {
156 return self.pointer.getDisplay()
157 }
158
159 access(all) fun getNFTCollectionData() : MetadataViews.NFTCollectionData {
160 return self.pointer.getNFTCollectionData()
161 }
162 }
163
164 access(all) resource interface SaleItemCollectionPublic {
165 //fetch all the tokens in the collection
166 access(all) fun getIds(): [UInt64]
167 access(all) fun containsId(_ id: UInt64): Bool
168 access(contract)fun cancelBid(_ id: UInt64)
169 access(contract) fun registerIncreasedBid(_ id: UInt64)
170
171 //place a bid on a token
172 access(contract) fun registerBid(item: FindViews.ViewReadPointer, callback: Capability<&MarketBidCollection>, validUntil: UFix64?, saleItemExtraField: {String : AnyStruct})
173 }
174
175 access(all) resource SaleItemCollection: SaleItemCollectionPublic, FindMarket.SaleItemCollectionPublic {
176 //is this the best approach now or just put the NFT inside the saleItem?
177 access(contract) var items: @{UInt64: SaleItem}
178
179 access(contract) let tenantCapability: Capability<&FindMarket.Tenant>
180
181 init (_ tenantCapability: Capability<&FindMarket.Tenant>) {
182 self.items <- {}
183 self.tenantCapability=tenantCapability
184 }
185
186 access(self) fun getTenant() : &FindMarket.Tenant {
187 if !self.tenantCapability.check() {
188 panic("Tenant client is not linked anymore")
189 }
190 return self.tenantCapability.borrow()!
191 }
192
193 access(all) fun getListingType() : Type {
194 return Type<@SaleItem>()
195 }
196
197 //this is called when a buyer cancel a direct offer
198 access(contract) fun cancelBid(_ id: UInt64) {
199 if !self.items.containsKey(id) {
200 panic("Invalid id=".concat(id.toString()))
201 }
202 let saleItem=self.borrow(id)
203
204 let tenant=self.getTenant()
205 let ftType= saleItem.getFtType()
206
207 let status="cancel"
208 let owner=self.owner!.address
209 let balance=saleItem.getBalance()
210 let buyer=saleItem.getBuyer()!
211 let buyerName=FIND.reverseLookup(buyer)
212 let profile = Profile.find(buyer)
213
214 var nftInfo:FindMarket.NFTInfo?=nil
215 if saleItem.checkPointer() {
216 nftInfo=saleItem.toNFTInfo(false)
217 }
218
219 emit DirectOffer(tenant:tenant.name, id: id, saleID: saleItem.uuid, seller:owner, sellerName: FIND.reverseLookup(owner), amount: balance, status:status, vaultType: ftType.identifier, nft:nftInfo, buyer: buyer, buyerName: buyerName, buyerAvatar: profile.getAvatar(), endsAt: saleItem.validUntil, previousBuyer:nil, previousBuyerName:nil)
220
221 destroy <- self.items.remove(key: id)
222 }
223
224 //The only thing we do here is basically register an event
225 access(contract) fun registerIncreasedBid(_ id: UInt64) {
226
227 if !self.items.containsKey(id) {
228 panic("Invalid id=".concat(id.toString()))
229 }
230 let saleItem=self.borrow(id)
231
232 let tenant=self.getTenant()
233 let nftType= saleItem.getItemType()
234 let ftType= saleItem.getFtType()
235
236 let actionResult=tenant.allowedAction(listingType: Type<@FindMarketDirectOfferEscrow.SaleItem>(), nftType: nftType, ftType: ftType, action: FindMarket.MarketAction(listing:true, name: "add bid in direct offer"), seller: self.owner!.address, buyer: saleItem.offerCallback.address)
237
238 if !actionResult.allowed {
239 panic(actionResult.message)
240 }
241
242 let status="active_offered"
243 let owner=self.owner!.address
244 let balance=saleItem.getBalance()
245 let buyer=saleItem.getBuyer()!
246 let buyerName=FIND.reverseLookup(buyer)
247 let profile = Profile.find(buyer)
248
249 let nftInfo=saleItem.toNFTInfo(true)
250
251 emit DirectOffer(tenant:tenant.name, id: id, saleID: saleItem.uuid, seller:owner, sellerName: FIND.reverseLookup(owner), amount: balance, status:status, vaultType: ftType.identifier, nft:nftInfo, buyer: buyer, buyerName: buyerName, buyerAvatar: profile.getAvatar(), endsAt: saleItem.validUntil, previousBuyer:nil, previousBuyerName:nil)
252
253 }
254
255 //This is a function that buyer will call (via his bid collection) to register the bicCallback with the seller
256 access(contract) fun registerBid(item: FindViews.ViewReadPointer, callback: Capability<&MarketBidCollection>, validUntil: UFix64?, saleItemExtraField: {String : AnyStruct}) {
257
258 let id = item.getUUID()
259
260 //If there are no bids from anybody else before we need to make the item
261 if !self.items.containsKey(id) {
262
263 let saleItem <- create SaleItem(pointer: item, callback: callback, validUntil: validUntil, saleItemExtraField: saleItemExtraField)
264
265 let tenant=self.getTenant()
266 let nftType= saleItem.getItemType()
267 let ftType= saleItem.getFtType()
268
269 let actionResult=tenant.allowedAction(listingType: Type<@FindMarketDirectOfferEscrow.SaleItem>(), nftType: nftType, ftType: ftType, action: FindMarket.MarketAction(listing:true, name: "bid in direct offer"), seller: self.owner!.address, buyer: callback.address)
270
271 if !actionResult.allowed {
272 panic(actionResult.message)
273 }
274
275 self.items[id] <-! saleItem
276 let saleItemRef=self.borrow(id)
277
278 let status="active_offered"
279 let owner=self.owner!.address
280 let balance=saleItemRef.getBalance()
281 let buyer=callback.address
282 let buyerName=FIND.reverseLookup(buyer)
283 let profile = Profile.find(buyer)
284
285 let nftInfo=saleItemRef.toNFTInfo(true)
286
287 emit DirectOffer(tenant:tenant.name, id: id, saleID: saleItemRef.uuid, seller:owner, sellerName: FIND.reverseLookup(owner), amount: balance, status:status, vaultType: ftType.identifier, nft:nftInfo, buyer: buyer, buyerName: buyerName, buyerAvatar: profile.getAvatar(), endsAt: saleItemRef.validUntil, previousBuyer:nil, previousBuyerName:nil)
288
289 return
290 }
291
292 let saleItem=self.borrow(id)
293
294 if self.borrow(id).getBuyer()! == callback.address {
295 panic("You already have the latest bid on this item, use the incraseBid transaction")
296 }
297
298 let tenant=self.getTenant()
299 let nftType= saleItem.getItemType()
300 let ftType= saleItem.getFtType()
301
302 let actionResult=tenant.allowedAction(listingType: Type<@FindMarketDirectOfferEscrow.SaleItem>(), nftType: nftType, ftType: ftType, action: FindMarket.MarketAction(listing:true, name: "bid in direct offer"), seller: self.owner!.address, buyer: callback.address)
303
304 if !actionResult.allowed {
305 panic(actionResult.message)
306 }
307
308 let balance=callback.borrow()?.getBalance(id) ?? panic("Bidder unlinked the bid collection capability. bidder address : ".concat(callback.address.toString()))
309
310 let currentBalance=saleItem.getBalance()
311 Debug.log("currentBalance=".concat(currentBalance.toString()).concat(" new bid is at=").concat(balance.toString()))
312 if currentBalance >= balance {
313 panic("There is already a higher bid on this item")
314 }
315 //somebody else has the highest item so we cancel it
316 let previousBuyer=saleItem.offerCallback.address
317 let previousCB = saleItem.offerCallback.borrow() ?? panic("Previous bidder unlinked the bid collection capability. bidder address : ".concat(previousBuyer.toString()))
318 previousCB.cancelBidFromSaleItem(id)
319 saleItem.setValidUntil(validUntil)
320 saleItem.setCallback(callback)
321
322 let status="active_offered"
323 let owner=self.owner!.address
324 let buyer=callback.address
325 let buyerName=FIND.reverseLookup(buyer)
326 let profile = Profile.find(buyer)
327
328 let nftInfo=saleItem.toNFTInfo(true)
329
330 let previousBuyerName = FIND.reverseLookup(previousBuyer)
331
332 emit DirectOffer(tenant:tenant.name, id: id, saleID: saleItem.uuid, seller:owner, sellerName: FIND.reverseLookup(owner), amount: balance, status:status, vaultType: ftType.identifier, nft:nftInfo, buyer: buyer, buyerName: buyerName, buyerAvatar: profile.getAvatar(), endsAt: saleItem.validUntil, previousBuyer:previousBuyer, previousBuyerName:previousBuyerName)
333
334
335 }
336
337 //cancel will reject a direct offer
338 access(Seller) fun cancel(_ id: UInt64) {
339
340 if !self.items.containsKey(id) {
341 panic("Invalid id=".concat(id.toString()))
342 }
343
344 let saleItem=self.borrow(id)
345
346 let tenant=self.getTenant()
347 let ftType= saleItem.getFtType()
348
349
350 var status="rejected"
351 let owner=self.owner!.address
352 let balance=saleItem.getBalance()
353 let buyer=saleItem.getBuyer()!
354 let buyerName=FIND.reverseLookup(buyer)
355 let profile = Profile.find(buyer)
356
357 var nftInfo:FindMarket.NFTInfo?=nil
358 if saleItem.checkPointer() {
359 nftInfo=saleItem.toNFTInfo(false)
360 }
361
362 emit DirectOffer(tenant:tenant.name, id: id, saleID: saleItem.uuid, seller:owner, sellerName: FIND.reverseLookup(owner), amount: balance, status:status, vaultType: ftType.identifier, nft:nftInfo, buyer: buyer, buyerName: buyerName, buyerAvatar: profile.getAvatar(), endsAt: saleItem.validUntil, previousBuyer:nil, previousBuyerName:nil)
363
364 saleItem.offerCallback.borrow()!.cancelBidFromSaleItem(id)
365 destroy <- self.items.remove(key: id)
366 }
367
368 access(Seller) fun acceptDirectOffer(_ pointer: FindViews.AuthNFTPointer) {
369
370 let id = pointer.getUUID()
371 if !self.items.containsKey(id) {
372 panic("Invalid id=".concat(pointer.getUUID().toString()))
373 }
374 let saleItem = self.borrow(id)
375
376 if saleItem.validUntil != nil && saleItem.validUntil! < Clock.time() {
377 panic("This direct offer is already expired")
378 }
379
380 let tenant=self.getTenant()
381 let nftType= saleItem.getItemType()
382 let ftType= saleItem.getFtType()
383
384 let actionResult=tenant.allowedAction(listingType: Type<@FindMarketDirectOfferEscrow.SaleItem>(), nftType: nftType, ftType: ftType, action: FindMarket.MarketAction(listing:false, name: "fulfill directOffer"), seller: self.owner!.address, buyer: saleItem.offerCallback.address)
385
386 if !actionResult.allowed {
387 panic(actionResult.message)
388 }
389
390 let cuts= tenant.getCuts(name: actionResult.name, listingType: Type<@FindMarketDirectOfferEscrow.SaleItem>(), nftType: nftType, ftType: ftType)
391
392 //Set the auth pointer in the saleItem so that it now can be fulfilled
393 saleItem.setPointer(pointer)
394
395 let royalty=saleItem.getRoyalty()
396 let nftInfo=saleItem.toNFTInfo(true)
397
398 let status="sold"
399 let owner=saleItem.getSeller()
400 let balance=saleItem.getBalance()
401 let buyer=saleItem.getBuyer()!
402 let buyerName=FIND.reverseLookup(buyer)
403 let sellerName=FIND.reverseLookup(owner)
404 let profile = Profile.find(buyer)
405
406 emit DirectOffer(tenant:tenant.name, id: id, saleID: saleItem.uuid, seller:owner, sellerName: sellerName , amount: balance, status:status, vaultType: ftType.identifier, nft:nftInfo, buyer: buyer, buyerName: buyerName, buyerAvatar: profile.getAvatar(), endsAt: saleItem.validUntil, previousBuyer:nil, previousBuyerName:nil)
407
408 let vault <- saleItem.acceptEscrowedBid()
409
410 let resolved : {Address : String} = {}
411 resolved[buyer] = buyerName ?? ""
412 resolved[owner] = sellerName ?? ""
413 resolved[FindMarketDirectOfferEscrow.account.address] = "find"
414 // Have to make sure the tenant always have the valid find name
415 resolved[FindMarket.tenantNameAddress[tenant.name]!] = tenant.name
416
417 FindMarket.pay(tenant: tenant.name, id:id, saleItem: saleItem, vault: <- vault, royalty:royalty, nftInfo:nftInfo, cuts:cuts, resolver: fun(address:Address): String? { return FIND.reverseLookup(address) }, resolvedAddress: resolved)
418 destroy <- self.items.remove(key: id)
419 }
420
421 access(all) fun getIds(): [UInt64] {
422 return self.items.keys
423 }
424
425 access(all) fun getRoyaltyChangedIds(): [UInt64] {
426 let ids : [UInt64] = []
427 for id in self.getIds() {
428 let item = self.borrow(id)
429 if !item.validateRoyalties() {
430 ids.append(id)
431 }
432 }
433 return ids
434 }
435
436 access(all) fun containsId(_ id: UInt64): Bool {
437 return self.items.containsKey(id)
438 }
439
440 access(all) fun borrow(_ id: UInt64): &SaleItem {
441 return (&self.items[id])!
442 }
443
444 access(all) fun borrowSaleItem(_ id: UInt64) : &{FindMarket.SaleItem} {
445 if !self.items.containsKey(id) {
446 panic("This id does not exist : ".concat(id.toString()))
447 }
448 return (&self.items[id])!
449 }
450 }
451
452 access(all) resource Bid : FindMarket.Bid {
453 access(contract) let from: Capability<&SaleItemCollection>
454 access(contract) let nftCap: Capability<&{NonFungibleToken.Receiver}>
455 access(contract) let itemUUID: UInt64
456
457 //this should reflect on what the above uuid is for
458 access(contract) let vault: @{FungibleToken.Vault}
459 access(contract) let vaultType: Type
460 access(contract) var bidAt: UFix64
461 access(contract) let bidExtraField: {String : AnyStruct}
462
463 init(from: Capability<&SaleItemCollection>, itemUUID: UInt64, vault: @{FungibleToken.Vault}, nftCap: Capability<&{NonFungibleToken.Receiver}>, bidExtraField: {String : AnyStruct}) {
464 self.vaultType=vault.getType()
465 self.vault <- vault
466 self.itemUUID=itemUUID
467 self.from=from
468 self.bidAt=Clock.time()
469 self.nftCap=nftCap
470 self.bidExtraField=bidExtraField
471 }
472
473 access(contract) fun setBidAt(_ time: UFix64) {
474 self.bidAt=time
475 }
476
477 access(all) fun getBalance() : UFix64 {
478 return self.vault.balance
479 }
480
481 access(all) fun getSellerAddress() : Address {
482 return self.from.address
483 }
484
485 access(all) fun getBidExtraField() : {String : AnyStruct} {
486 return self.bidExtraField
487 }
488 }
489
490 access(all) resource interface MarketBidCollectionPublic {
491 access(all) fun getBalance(_ id: UInt64) : UFix64
492 access(all) fun getVaultType(_ id: UInt64) : Type
493 access(all) fun containsId(_ id: UInt64): Bool
494 access(contract) fun accept(_ nft: @{NonFungibleToken.NFT}, path:PublicPath) : @{FungibleToken.Vault}
495 access(contract) fun cancelBidFromSaleItem(_ id: UInt64)
496 }
497
498 access(all) entitlement Buyer
499
500 //A collection stored for bidders/buyers
501 access(all) resource MarketBidCollection: MarketBidCollectionPublic, FindMarket.MarketBidCollectionPublic {
502
503 access(contract) var bids : @{UInt64: Bid}
504 access(contract) let receiver: Capability<&{FungibleToken.Receiver}>
505 access(contract) let tenantCapability: Capability<&FindMarket.Tenant>
506
507 //not sure we can store this here anymore. think it needs to be in every bid
508 init(receiver: Capability<&{FungibleToken.Receiver}>, tenantCapability: Capability<&FindMarket.Tenant>) {
509 self.bids <- {}
510 self.receiver=receiver
511 self.tenantCapability=tenantCapability
512 }
513
514 access(self) fun getTenant() : &FindMarket.Tenant {
515 if !self.tenantCapability.check() {
516 panic("Tenant client is not linked anymore")
517 }
518 return self.tenantCapability.borrow()!
519 }
520
521 access(contract) fun accept(_ nft: @{NonFungibleToken.NFT}, path:PublicPath) : @{FungibleToken.Vault} {
522 let id= nft.id
523 let bid <- self.bids.remove(key: nft.uuid) ?? panic("missing bid")
524 let vaultRef = &bid.vault as auth(FungibleToken.Withdraw) &{FungibleToken.Vault}
525
526 let nftCap = bid.nftCap
527 if !nftCap.check() {
528 let cpCap =getAccount(nftCap.address).capabilities.get<&{NonFungibleToken.Collection}>(path)!
529 if !cpCap.check() {
530 panic("Bidder unlinked the nft receiver capability. bidder address : ".concat(bid.nftCap.address.toString()))
531 } else {
532 bid.nftCap.borrow()!.deposit(token: <- nft)
533 }
534 } else {
535 bid.nftCap.borrow()!.deposit(token: <- nft)
536 }
537
538 let vault <- vaultRef.withdraw(amount: vaultRef.balance)
539 destroy bid
540 return <- vault
541 }
542
543 access(all) fun getVaultType(_ id:UInt64) : Type {
544 return self.borrowBid(id).vaultType
545 }
546
547 access(all) fun getIds() : [UInt64] {
548 return self.bids.keys
549 }
550
551 access(all) fun containsId(_ id: UInt64) : Bool {
552 return self.bids.containsKey(id)
553 }
554
555 access(all) fun getBidType() : Type {
556 return Type<@Bid>()
557 }
558
559 access(Buyer) fun bid(item: FindViews.ViewReadPointer, vault: @{FungibleToken.Vault}, nftCap: Capability<&{NonFungibleToken.Receiver}>, validUntil: UFix64?, saleItemExtraField: {String : AnyStruct}, bidExtraField: {String : AnyStruct}) {
560
561 // ensure it is not a 0 dollar listing
562 if vault.balance <= 0.0 {
563 panic("Offer price should be greater than 0")
564 }
565
566 // ensure validUntil is valid
567 if validUntil != nil && validUntil! < Clock.time() {
568 panic("Valid until is before current time")
569 }
570
571 // check soul bound
572 if item.checkSoulBound() {
573 panic("This item is soul bounded and cannot be traded")
574 }
575
576 if self.owner!.address == item.owner() {
577 panic("You cannot bid on your own resource")
578 }
579
580 let uuid=item.getUUID()
581
582 if self.bids[uuid] != nil {
583 panic("You already have an bid for this item, use increaseBid on that bid")
584 }
585 let tenant=self.getTenant()
586
587 // Check if it is onefootball. If so, listing has to be at least $0.65 (DUC)
588 if tenant.name == "onefootball" {
589 // ensure it is not a 0 dollar listing
590 if vault.balance <= 0.65 {
591 panic("Offer price should be greater than 0.65")
592 }
593 }
594 let from=getAccount(item.owner()).capabilities.get<&SaleItemCollection>(tenant.getPublicPath(Type<@SaleItemCollection>()))!
595
596 let bid <- create Bid(from: from, itemUUID:item.getUUID(), vault: <- vault, nftCap: nftCap, bidExtraField: bidExtraField)
597 let saleItemCollection= from.borrow() ?? panic("Could not borrow sale item for id=".concat(uuid.toString()))
598 let callbackCapability =self.owner!.capabilities.get<&MarketBidCollection>(tenant.getPublicPath(Type<@MarketBidCollection>()))!
599 let oldToken <- self.bids[uuid] <- bid
600 saleItemCollection.registerBid(item: item, callback: callbackCapability, validUntil:validUntil, saleItemExtraField: saleItemExtraField)
601 destroy oldToken
602 }
603
604 access(Buyer) fun increaseBid(id: UInt64, vault: @{FungibleToken.Vault}) {
605 let bid =self.borrowBid(id)
606 bid.setBidAt(Clock.time())
607 bid.vault.deposit(from: <- vault)
608 if !bid.from.check() {
609 panic("Seller unlinked SaleItem collection capability. seller address : ".concat(bid.from.address.toString()))
610 }
611 bid.from.borrow()!.registerIncreasedBid(id)
612 }
613
614 /// The users cancel a bid himself
615 access(Buyer) fun cancelBid(_ id: UInt64) {
616 let bid= self.borrowBid(id)
617 bid.from.borrow()!.cancelBid(id)
618 self.cancelBidFromSaleItem(id)
619 }
620
621 access(contract) fun cancelBidFromSaleItem(_ id: UInt64) {
622 if !self.receiver.check() {
623 panic("This user does not have receiver vault set up. User: ".concat(self.receiver.address.toString()))
624 }
625 Debug.log("cancel bid")
626 let bid <- self.bids.remove(key: id) ?? panic("missing bid")
627 let vaultRef = &bid.vault as auth(FungibleToken.Withdraw) &{FungibleToken.Vault}
628 self.receiver.borrow()!.deposit(from: <- vaultRef.withdraw(amount: vaultRef.balance))
629 destroy bid
630 }
631
632 access(all) fun borrowBid(_ id: UInt64): &Bid {
633 if !self.bids.containsKey(id){
634 panic("This id does not exist : ".concat(id.toString()))
635 }
636 return (&self.bids[id])!
637 }
638
639 access(all) fun borrowBidItem(_ id: UInt64): &{FindMarket.Bid} {
640 if !self.bids.containsKey(id){
641 panic("This id does not exist : ".concat(id.toString()))
642 }
643 return (&self.bids[id])!
644 }
645
646 access(all) fun getBalance(_ id: UInt64) : UFix64 {
647 let bid= self.borrowBid(id)
648 return bid.vault.balance
649 }
650 }
651 //Create an empty lease collection that store your leases to a name
652 access(all) fun createEmptySaleItemCollection(_ tenantCapability: Capability<&FindMarket.Tenant>): @SaleItemCollection {
653 return <- create SaleItemCollection(tenantCapability)
654 }
655
656 access(all) fun createEmptyMarketBidCollection(receiver: Capability<&{FungibleToken.Receiver}>, tenantCapability: Capability<&FindMarket.Tenant>) : @MarketBidCollection {
657 return <- create MarketBidCollection(receiver: receiver, tenantCapability:tenantCapability)
658 }
659
660 access(all) fun getSaleItemCapability(marketplace:Address, user:Address) : Capability<&SaleItemCollection>? {
661 if FindMarket.getTenantCapability(marketplace) == nil {
662 panic("Invalid tenant")
663 }
664 if let tenant=FindMarket.getTenantCapability(marketplace)!.borrow() {
665 return getAccount(user).capabilities.get<&SaleItemCollection>(tenant.getPublicPath(Type<@SaleItemCollection>()))
666 }
667 return nil
668 }
669
670 access(all) fun getSaleItemExtraField(marketplace:Address, user:Address, id:UInt64) : {String : AnyStruct}? {
671 if FindMarket.getTenantCapability(marketplace) == nil {
672 panic("Invalid tenant")
673 }
674 if let tenant=FindMarket.getTenantCapability(marketplace)!.borrow() {
675 if let saleItemCollection = getAccount(user).capabilities.get<&{FindMarket.SaleItemCollectionPublic}>(tenant.getPublicPath(Type<@SaleItemCollection>()))!.borrow() {
676 if let saleItem = saleItemCollection.borrowSaleItem(id) {
677 return saleItem.getSaleItemExtraField()
678 }
679 }
680 }
681 return nil
682 }
683
684 access(all) fun getBidCapability( marketplace:Address, user:Address) : Capability<&MarketBidCollection>? {
685 if FindMarket.getTenantCapability(marketplace) == nil {
686 panic("Invalid tenant")
687 }
688 if let tenant=FindMarket.getTenantCapability(marketplace)!.borrow() {
689 return getAccount(user).capabilities.get<&MarketBidCollection>(tenant.getPublicPath(Type<@MarketBidCollection>()))
690 }
691 return nil
692 }
693
694 init() {
695 FindMarket.addSaleItemType(Type<@SaleItem>())
696 FindMarket.addSaleItemCollectionType(Type<@SaleItemCollection>())
697 FindMarket.addMarketBidType(Type<@Bid>())
698 FindMarket.addMarketBidCollectionType(Type<@MarketBidCollection>())
699 }
700}
701