Smart Contract

TrmMarketV2_2

A.61fc4b873e58733b.TrmMarketV2_2

Deployed

1d ago
Feb 26, 2026, 09:44:01 PM UTC

Dependents

0 imports
1/**
2
3    TrmMarketV2_2.cdc
4
5    Description: Contract definitions for users to sell and/or rent out their assets
6
7    Marketplace is where users can create a sale collection that they
8    store in their account storage. In the sale collection, 
9    they can put their NFTs up for sale/rent with a price and publish a 
10    reference so that others can see the sale.
11
12    If another user sees an NFT that they want to buy,
13    they can send fungible tokens that equal the buy price
14    to buy the NFT.  The NFT is transferred to them when
15    they make the purchase.
16
17    If another user sees an NFT that they want to rent,
18    they can send fungible tokens that equal the rent price
19    to rent the NFT. The Rent NFT will be minted and 
20    transferred to them.
21
22    Each user who wants to sell/rent out tokens will have a sale 
23    collection instance in their account that contains price information 
24    for each node in their collection. The sale holds a capability that 
25    links to their main asset collection.
26
27    They can give a reference to this collection to a central contract
28    so that it can list the sales in a central place
29
30    When a user creates a sale, they will supply four arguments:
31    - A TrmAssetV2_2.Collection capability that allows their sale to withdraw an asset when it is purchased.
32    - A FungibleToken.Receiver capability as the place where the payment for the token goes.
33    - A FungibleToken.Receiver capability specifying a beneficiary, where a cut of the purchase gets sent. 
34    - A cut percentage, specifying how much the beneficiary will recieve.
35    
36    The cut percentage can be set to zero if the user desires and they 
37    will receive the entirety of the purchase. Trm will initialize sales 
38    for users with the Trm admin vault as the vault where cuts get 
39    deposited to.
40**/
41
42import TrmAssetV2_2 from 0x61fc4b873e58733b
43import TrmRentV2_2 from 0x61fc4b873e58733b
44import NonFungibleToken from 0x1d7e57aa55817448
45
46
47access(all) contract TrmMarketV2_2 {
48    /// -----------------------------------------------------------------------
49    /// TRM Market contract Event definitions
50    /// -----------------------------------------------------------------------
51
52    /// Event that emitted when the NFT contract is initialized
53    access(all) event ContractInitialized()
54    // Event that emitted when the sale collection is initialized
55    access(all) event SaleCollectionInitialized(userAccountAddress: Address)
56    /// Emitted when an Asset is listed for transfer
57    access(all) event AssetListedForTransfer(assetTokenID: UInt64, salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?, seller: Address?)
58    /// Emitted when an Asset is listed for transfer
59    access(all) event AssetBatchListedForTransfer(assetTokenIDs: [UInt64], salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?, seller: Address?)
60    /// Emitted when an Asset is listed for rent
61    access(all) event AssetListedForRent(assetTokenID: UInt64, price: UFix64?, rentalPeriodSeconds: UFix64, seller: Address?)
62    /// Emitted when an Asset is listed for rent
63    access(all) event AssetBatchListedForRent(assetTokenIDs: [UInt64], price: UFix64?, rentalPeriodSeconds: UFix64, seller: Address?)
64    /// Emitted when the sale price of a listed asset has changed
65    access(all) event AssetTransferListingChanged(assetTokenID: UInt64, salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?, seller: Address?)
66    /// Emitted when the sale price of a listed asset has changed
67    access(all) event AssetBatchTransferListingChanged(assetTokenIDs: [UInt64], salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?, seller: Address?)
68    /// Emitted when the rent price of a listed asset has changed
69    access(all) event AssetRentListingChanged(assetTokenID: UInt64, price: UFix64?, rentalPeriodSeconds: UFix64, seller: Address?)
70    /// Emitted when the rent price of a listed asset has changed
71    access(all) event AssetBatchRentListingChanged(assetTokenIDs: [UInt64], price: UFix64?, rentalPeriodSeconds: UFix64, seller: Address?)
72    /// Emitted when a token is purchased from the market
73    access(all) event AssetTransferred(assetTokenID: UInt64, price: UFix64, seller: Address?, buyer: Address?, paymentID: String)
74    /// Emitted when a token is rented from the market
75    access(all) event AssetRented(assetTokenID: UInt64, rentID: UInt64, price: UFix64, expiryTimestamp: UFix64, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetMetadata: {String: String}, kID: String, seller: Address?, buyer: Address?, rentalPeriodSeconds: UFix64, paymentID: String)
76    /// Emitted when a token has been delisted for sale
77    access(all) event AssetDelistedForTransfer(assetTokenID: UInt64, owner: Address?)
78    /// Emitted when a token has been delisted for sale
79    access(all) event AssetBatchDelistedForTransfer(assetTokenIDs: [UInt64], owner: Address?)
80    /// Emitted when a token has been delisted for rent
81    access(all) event AssetDelistedForRent(assetTokenID: UInt64, owner: Address?)
82    /// Emitted when a token has been delisted for rent
83    access(all) event AssetBatchDelistedForRent(assetTokenIDs: [UInt64], owner: Address?)
84
85    /// Path where the `SaleCollection` is stored
86    access(all) let marketStoragePath: StoragePath
87    /// Path where the public capability for the `SaleCollection` is
88    access(all) let marketPublicPath: PublicPath
89   
90    /// Path where the 'Admin' resource is stored
91    access(all) let adminStoragePath: StoragePath
92
93
94    access(all) resource RentListing {
95        access(all) var price: UFix64
96        access(all) var rentalPeriodSeconds: UFix64
97
98        access(contract) fun setRentalPeriodSeconds(seconds: UFix64) {
99            self.rentalPeriodSeconds = seconds
100        }
101
102        init(rentalPeriodSeconds: UFix64) {
103            self.price = 0.0
104            self.rentalPeriodSeconds = rentalPeriodSeconds
105        }
106    }
107
108    access(all) resource TransferListing {
109        access(all) var price: UFix64
110
111        init() {
112            self.price = 0.0
113        }
114    }
115
116    /// SalePublic 
117    //
118    /// The interface that a user can publish a capability to their sale
119    /// to allow others to access their sale
120    access(all) resource interface SalePublic {
121        access(contract) fun listForTransfer(tokenID: UInt64, salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?)
122        access(contract) fun listForRent(tokenID: UInt64, price: UFix64?, rentalPeriodSeconds: UFix64?)
123        access(contract) fun batchListForTransfer(tokenIDs: [UInt64], salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?)
124        access(contract) fun batchListForRent(tokenIDs: [UInt64], price: UFix64?, rentalPeriodSeconds: UFix64?)
125        access(contract) fun cancelTransfer(tokenID: UInt64)
126        access(contract) fun cancelRent(tokenID: UInt64)
127        access(contract) fun batchCancelTransfer(tokenIDs: [UInt64])
128        access(contract) fun batchCancelRent(tokenIDs: [UInt64])
129        access(contract) fun transfer(tokenID: UInt64, adminAccount: &TrmAssetV2_2.Admin, recipientAddress: Address, price: UFix64, paymentID: String)
130        access(contract) fun rent(tokenID: UInt64, price: UFix64, recipient: Capability<&TrmRentV2_2.Collection>, paymentID: String): UInt64
131
132        access(all) fun getTransferListing(tokenID: UInt64): &TransferListing?
133        access(all) fun getRentListing(tokenID: UInt64): &RentListing?
134        access(all) fun getTransferIDs(): [UInt64]
135        access(all) fun getRentIDs(): [UInt64]
136        access(all) fun borrowAsset(id: UInt64): &TrmAssetV2_2.NFT? {
137            /// If the result isn't nil, the id of the returned reference
138            /// should be the same as the argument to the function
139            post {
140                (result == nil) || (result?.id == id): 
141                    "Cannot borrow Asset reference: The ID of the returned reference is incorrect"
142            }
143        }
144    }
145
146    /// SaleCollection
147    ///
148    /// This is the main resource that token sellers will store in their account
149    /// to manage the NFTs that they are selling/renting. The SaleCollection keeps 
150    /// track of the price of each token.
151    /// 
152    /// When a token is purchased, a cut is taken from the tokens
153    /// and sent to the beneficiary, then the rest are sent to the seller.
154    ///
155    /// The seller chooses who the beneficiary is and what percentage
156    /// of the tokens gets taken from the purchase
157    access(all) resource SaleCollection: SalePublic {
158
159        /// A reference to collection of the user's assets 
160        access(self) var ownerCollection: Capability<&TrmAssetV2_2.Collection>
161
162        /// Dictionary of the transfer prices for each NFT by ID
163        access(self) var transferListings: @{UInt64: TransferListing}
164
165        /// Dictionary of the rent prices for each NFT by ID
166        access(self) var rentListings: @{UInt64: RentListing}
167
168        init (ownerCollection: Capability<&TrmAssetV2_2.Collection>) {
169            pre {
170                /// Check that the owner's asset collection capability is correct
171                ownerCollection.check(): 
172                    "Owner's Asset Collection Capability is invalid!"
173            }
174            
175            /// create an empty collection to store the assets that are for transfer and rent
176            self.ownerCollection = ownerCollection
177            
178            /// prices are initially empty because there are no assets for transfer and rent
179            self.transferListings <- {}
180            self.rentListings <- {}
181        }
182
183        //destroy() {
184        //    destroy self.transferListings
185        //    destroy self.rentListings
186        //}
187
188        /// listForTransfer lists an NFT for transfer in this transfer collection at the specified price, or updates an existing listing
189        ///
190        /// Parameters: tokenID: The id of the NFT to be put up for transfer
191        ///             salePrice: The sale price of the NFT(This is just returned back as an event)
192        ///             auctionPrice: The auction price of the NFT(This is just returned back as an event)
193        ///             auctionStartTimestamp: The auction start timestamp
194        ///             auctionPeriodSeconds: The period for which auction will last
195        access(contract) fun listForTransfer(tokenID: UInt64, salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?) {
196            pre {
197                self.ownerCollection.borrow()!.idExists(id: tokenID):
198                    "Asset does not exist in the owner's collection"
199            }
200
201            if self.transferListings.containsKey(tokenID) {
202                emit AssetTransferListingChanged(assetTokenID: tokenID, salePrice: salePrice, auctionPrice: auctionPrice, auctionStartTimestamp: auctionStartTimestamp, auctionPeriodSeconds: auctionPeriodSeconds, seller: self.owner?.address)
203            } else {
204                let oldTransferListing <- self.transferListings[tokenID] <- create TransferListing()
205                destroy oldTransferListing
206
207                emit AssetListedForTransfer(assetTokenID: tokenID, salePrice: salePrice, auctionPrice: auctionPrice, auctionStartTimestamp: auctionStartTimestamp, auctionPeriodSeconds: auctionPeriodSeconds, seller: self.owner?.address)
208            }
209        }
210
211        /// listForRent lists an NFT for rent in this sale collection at the specified price and rental period, or updates an existing listing
212        ///
213        /// Parameters: tokenID: The id of the NFT to be put up for rent
214        ///             price: The rent price of the NFT(This is just returned back as an event)
215        ///             rentalPeriodSeconds: The rental period (in seconds)
216        access(contract) fun listForRent(tokenID: UInt64, price: UFix64?, rentalPeriodSeconds: UFix64?) {
217            pre {
218                self.ownerCollection.borrow()!.idExists(id: tokenID):
219                    "Asset does not exist in the owner's collection"
220            }
221
222            if let rentListing <- self.rentListings.remove(key: tokenID) {
223                if(rentalPeriodSeconds != nil) {
224                    rentListing.setRentalPeriodSeconds(seconds: rentalPeriodSeconds!)
225                }
226
227                let oldRentListing <- self.rentListings[tokenID] <- rentListing
228                destroy oldRentListing
229
230                let rentListingRef = (&self.rentListings[tokenID] as &RentListing?)!
231
232                emit AssetRentListingChanged(assetTokenID: tokenID, price: price, rentalPeriodSeconds: rentListingRef.rentalPeriodSeconds, seller: self.owner?.address)
233            } else {
234                assert(rentalPeriodSeconds != nil, message: "Rental period must be supplied for new listing")
235
236                let oldRentListing <- self.rentListings[tokenID] <- create RentListing(rentalPeriodSeconds: rentalPeriodSeconds!)
237                destroy oldRentListing
238
239                emit AssetListedForRent(assetTokenID: tokenID, price: price, rentalPeriodSeconds: rentalPeriodSeconds!, seller: self.owner?.address)
240            }
241        }
242
243        /// batchListForTransfer lists an array of NFTs for transfer in this transfer collection at the specified price, or updates existing listings
244        ///
245        /// Parameters: tokenIDs: The array of NFT IDs to be put up for transfer
246        ///             salePrice: The sale price of the NFT(This is just returned back as an event)
247        ///             auctionPrice: The auction price of the NFT(This is just returned back as an event)
248        ///             auctionStartTimestamp: The auction start timestamp
249        ///             auctionPeriodSeconds: The period for which auction will last
250        access(contract) fun batchListForTransfer(tokenIDs: [UInt64], salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?) {
251
252            let existingTransferListings: [UInt64] = []
253            let nonExistingTransferListings: [UInt64] = []
254
255            for tokenID in tokenIDs {
256                if (!self.ownerCollection.borrow()!.idExists(id: tokenID)) {
257                    panic("Asset does not exist in the owner's collection: ".concat(tokenID.toString()))
258                }
259
260                if self.transferListings.containsKey(tokenID) {
261                    existingTransferListings.append(tokenID)
262                } else {
263                    let oldTransferListing <- self.transferListings[tokenID] <- create TransferListing()
264                    destroy oldTransferListing
265
266                    nonExistingTransferListings.append(tokenID)
267                }
268            }
269
270            if (nonExistingTransferListings.length > 0) {
271                emit AssetBatchListedForTransfer(assetTokenIDs: nonExistingTransferListings, salePrice: salePrice, auctionPrice: auctionPrice, auctionStartTimestamp: auctionStartTimestamp, auctionPeriodSeconds: auctionPeriodSeconds, seller: self.owner?.address)
272            }
273
274            if (existingTransferListings.length > 0) {
275                emit AssetBatchTransferListingChanged(assetTokenIDs: existingTransferListings, salePrice: salePrice, auctionPrice: auctionPrice, auctionStartTimestamp: auctionStartTimestamp, auctionPeriodSeconds: auctionPeriodSeconds, seller: self.owner?.address)
276            }
277        }
278
279        /// batchListForRent lists an array of NFTs for rent in this sale collection at the specified price and rental period, or updates existing listings
280        ///
281        /// Parameters: tokenIDs: The array of NFT IDs to be put up for rent
282        ///             price: The rent price of the NFT(This is just returned back as an event)
283        ///             rentalPeriodSeconds: The rental period (in seconds)
284        access(contract) fun batchListForRent(tokenIDs: [UInt64], price: UFix64?, rentalPeriodSeconds: UFix64?) {
285
286            let existingRentListings: [UInt64] = []
287            let nonExistingRentListings: [UInt64] = []
288
289            for tokenID in tokenIDs {
290                if (!self.ownerCollection.borrow()!.idExists(id: tokenID)) {
291                    panic("Asset does not exist in the owner's collection: ".concat(tokenID.toString()))
292                }
293
294                if let rentListing <- self.rentListings.remove(key: tokenID) {
295                    if(rentalPeriodSeconds != nil) {
296                        rentListing.setRentalPeriodSeconds(seconds: rentalPeriodSeconds!)
297                    }
298
299                    let oldRentListing <- self.rentListings[tokenID] <- rentListing
300                    destroy oldRentListing
301
302                    let rentListingRef = (&self.rentListings[tokenID] as &RentListing?)!
303
304                    existingRentListings.append(tokenID)
305                } else {
306                    assert(rentalPeriodSeconds != nil, message: "Rental period must be supplied for new listing")
307
308                    let oldRentListing <- self.rentListings[tokenID] <- create RentListing(rentalPeriodSeconds: rentalPeriodSeconds!)
309                    destroy oldRentListing
310
311                    nonExistingRentListings.append(tokenID)
312                }
313            }
314
315            if (nonExistingRentListings.length > 0) {
316                emit AssetBatchListedForRent(assetTokenIDs: nonExistingRentListings, price: price, rentalPeriodSeconds: rentalPeriodSeconds!, seller: self.owner?.address)
317            }
318
319            if (existingRentListings.length > 0) {
320                emit AssetBatchRentListingChanged(assetTokenIDs: existingRentListings, price: price, rentalPeriodSeconds: rentalPeriodSeconds!, seller: self.owner?.address)
321            }
322        }
323
324        /// cancelTransfer cancels an asset transfer and clears its price
325        ///
326        /// Parameters: tokenID: the ID of the token to remove from the transfer
327        access(contract) fun cancelTransfer(tokenID: UInt64) {
328            pre {
329                self.transferListings[tokenID] != nil:
330                    "Asset not listed for transfer"
331            }
332
333            /// Remove the listing from the listing dictionary
334            let oldTransferListing <- self.transferListings.remove(key: tokenID)
335            destroy oldTransferListing
336            
337            /// Emit the event for delisting a moment from the Transfer
338            emit AssetDelistedForTransfer(assetTokenID: tokenID, owner: self.owner?.address)
339        }
340
341        /// cancelRent cancels an asset rent and clears its price
342        ///
343        /// Parameters: tokenID: the ID of the token to remove from the sale
344        access(contract) fun cancelRent(tokenID: UInt64) {
345            pre {
346                self.rentListings[tokenID] != nil:
347                    "Asset not listed for rent"
348            }
349
350            /// Remove the listing from the listings dictionary
351            let oldRentListing <- self.rentListings.remove(key: tokenID)
352            destroy oldRentListing
353            
354            /// Emit the event for delisting an asset for rent
355            emit AssetDelistedForRent(assetTokenID: tokenID, owner: self.owner?.address)
356        }
357
358        /// batchCancelTransfer cancels the transfer listings for the array of NFTs
359        ///
360        /// Parameters: tokenIDs: The array of NFT IDs to be removed for transfer
361        access(contract) fun batchCancelTransfer(tokenIDs: [UInt64]) {
362            for tokenID in tokenIDs {
363                if (self.transferListings[tokenID] == nil) {
364                    panic("Asset not listed for transfer: ".concat(tokenID.toString()))
365                }
366
367                /// Remove the listing from the listing dictionary
368                let oldTransferListing <- self.transferListings.remove(key: tokenID)
369                destroy oldTransferListing
370            }
371
372            /// Emit the event for delisting a moment from the Transfer
373            emit AssetBatchDelistedForTransfer(assetTokenIDs: tokenIDs, owner: self.owner?.address)
374        }
375
376        /// batchCancelRent cancels the rent listings for the array of NFTs
377        ///
378        /// Parameters: tokenIDs: The array of NFT IDs to be removed for rent
379        access(contract) fun batchCancelRent(tokenIDs: [UInt64]) {
380            for tokenID in tokenIDs {
381                if (self.rentListings[tokenID] == nil) {
382                    panic("Asset not listed for rent: ".concat(tokenID.toString()))
383                }
384
385                /// Remove the listing from the listings dictionary
386                let oldRentListing <- self.rentListings.remove(key: tokenID)
387                destroy oldRentListing
388            }
389
390            /// Emit the event for delisting an asset for rent
391            emit AssetBatchDelistedForRent(assetTokenIDs: tokenIDs, owner: self.owner?.address)
392        }
393
394        /// purchase lets a user send tokens to purchase an NFT that is for sale
395        /// the purchased NFT is returned to the transaction context that called it
396        ///
397        /// Parameters: tokenID: the ID of the NFT to purchase
398        ///             adminAccount: Admin Account
399        ///             recipientAddress: Recipient Address
400        ///             price: transaction price for the transfer
401        ///             paymentID
402        access(contract) fun transfer(tokenID: UInt64, adminAccount: &TrmAssetV2_2.Admin, recipientAddress: Address, price: UFix64, paymentID: String) {
403            pre {
404                self.transferListings.containsKey(tokenID):
405                    "No token matching this ID for transfer!"
406
407                self.owner?.address != recipientAddress:
408                    "The recipient and owner cannot be same"
409            }
410
411            let transferListingRef = (&self.transferListings[tokenID] as &TransferListing?)!
412
413            /// Remove the transfer listing
414            if let transferListing <- self.transferListings.remove(key: tokenID) {
415                destroy transferListing
416            }
417
418            /// Remove for rent listing
419            if let rentListing <- self.rentListings.remove(key: tokenID) {
420                destroy rentListing
421            }
422
423            let boughtAsset <- adminAccount.withdrawAsset(assetCollectionAddress: self.owner!.address, id: tokenID)
424
425            adminAccount.depositAsset(assetCollectionAddress: recipientAddress, token: <-boughtAsset)
426
427            emit AssetTransferred(assetTokenID: tokenID, price: price, seller: self.owner?.address, buyer: recipientAddress, paymentID: paymentID)
428        }
429
430        /// rent lets a user send tokens to rent an NFT that is for rent
431        /// the newly minted rent NFT is returned to the transaction context that called it
432        ///
433        /// Parameters: tokenID: the ID of the NFT to purchase
434        ///             price: transaction price for the rent
435        ///             recipient: Recipient Collection to receive Rent Token
436        access(contract) fun rent(tokenID: UInt64, price: UFix64, recipient: Capability<&TrmRentV2_2.Collection>, paymentID: String): UInt64 {
437            pre {
438                self.rentListings.containsKey(tokenID):
439                    "No token matching this ID for rent!"
440
441                recipient.check():
442                    "Invalid receiver capability"
443
444                recipient.borrow()!.owner?.address != self.ownerCollection.borrow()!.owner?.address:
445                    "The recipient and owner cannot be same"
446            }
447
448            let rentListingRef = (&self.rentListings[tokenID] as &RentListing?)!
449
450            /// Read the rental period for the token
451            let rentalPeriodSeconds = rentListingRef.rentalPeriodSeconds
452
453            let ownerCollectionRef = self.ownerCollection.borrow()!
454            
455            assert(ownerCollectionRef.idExists(id: tokenID) == true, message: "Specified token ID not found in owner collection")
456
457            let expiry = rentalPeriodSeconds + getCurrentBlock().timestamp
458
459            let minterRef = TrmMarketV2_2.account.storage.borrow<&TrmRentV2_2.Minter>(from: TrmRentV2_2.minterStoragePath)!
460
461            let kID = ownerCollectionRef.getKID(id: tokenID)
462            let assetName = ownerCollectionRef.getAssetName(id: tokenID)
463            let assetDescription = ownerCollectionRef.getAssetDescription(id: tokenID)
464            let assetURL = ownerCollectionRef.getAssetURL(id: tokenID)
465            let assetThumbnailURL = ownerCollectionRef.getAssetThumbnailURL(id: tokenID)
466            // let assetMetadata = ownerCollectionRef.getAssetMetadata(id: tokenID)
467            let assetMetadata: {String: String} = {}
468
469            let rentTokenID = minterRef.mintNFT(assetTokenID: tokenID, kID: kID, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetMetadata: assetMetadata, expiryTimestamp: expiry, recipient: recipient.borrow()!)
470
471            emit AssetRented(assetTokenID: tokenID, rentID: rentTokenID, price: price, expiryTimestamp: expiry, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetMetadata: assetMetadata, kID: kID, seller: self.owner?.address, buyer: recipient.borrow()!.owner?.address, rentalPeriodSeconds: rentalPeriodSeconds, paymentID: paymentID)
472
473            return rentTokenID
474        }
475
476        /// getTransferListing returns the transfer listing of a specific token in the collection
477        /// 
478        /// Parameters: tokenID: The ID of the NFT whose listing to get
479        ///
480        /// Returns: TransferListing: The transfer listing of the token including price
481        access(all) fun getTransferListing(tokenID: UInt64): &TransferListing? {
482            if self.transferListings.containsKey(tokenID) {
483                return &self.transferListings[tokenID] as &TransferListing?
484            }
485            return nil
486        }
487
488        /// getRentListing returns the rent listing of a specific token in the collection
489        /// 
490        /// Parameters: tokenID: The ID of the NFT whose listing to get
491        ///
492        /// Returns: RentListing: The rent listing of the token including price, rental period
493        access(all) fun getRentListing(tokenID: UInt64): &RentListing? {
494            if self.rentListings.containsKey(tokenID) {
495                return &self.rentListings[tokenID] as &RentListing?
496            }
497            return nil
498        }
499
500        /// getTransferIDs returns an array of token IDs that are for transfer
501        access(all) fun getTransferIDs(): [UInt64] {
502            return self.transferListings.keys
503        }
504
505        /// getRentIDs returns an array of token IDs that are for sale
506        access(all) fun getRentIDs(): [UInt64] {
507            return self.rentListings.keys
508        }
509
510        /// borrowAsset Returns a borrowed reference to an Asset in the Collection so that the caller can read data from it
511        ///
512        /// Parameters: id: The ID of the token to borrow a reference to
513        ///
514        /// Returns: &TrmAssetV2_2.NFT? Optional reference to a token for transfer so that the caller can read its data
515        access(all) fun borrowAsset(id: UInt64): &TrmAssetV2_2.NFT? {
516            /// first check this collection
517            if self.transferListings[id] != nil || self.rentListings[id] != nil {
518                let ref = self.ownerCollection.borrow()!.borrowAsset(id: id)
519                return ref
520            } 
521            return nil
522        }
523    }
524
525    /// createCollection returns a new collection resource to the caller
526    access(all) fun createSaleCollection(ownerCollection: Capability<&TrmAssetV2_2.Collection>): @SaleCollection {
527
528        return <- create SaleCollection(ownerCollection: ownerCollection)
529    }
530
531    // emitCreateSaleCollectionEvent emits events for asset collection initialization
532    access(all) fun emitCreateSaleCollectionEvent(userAccountAddress: Address) {
533        emit SaleCollectionInitialized(userAccountAddress: userAccountAddress)
534    }
535
536    /// Admin is a special authorization resource that 
537    /// allows the admin to perform important functions
538    access(all) resource Admin {
539
540        access(all) fun listForTransfer(
541            saleCollectionAddress: Address, 
542            tokenID: UInt64, 
543            salePrice: UFix64?, 
544            auctionPrice: UFix64?,
545            auctionStartTimestamp: UFix64?,
546            auctionPeriodSeconds: UFix64?
547        ) {
548            let saleCollectionCapability = getAccount(saleCollectionAddress).capabilities.borrow<&TrmMarketV2_2.SaleCollection>(
549                TrmMarketV2_2.marketPublicPath
550                )
551            ?? panic("Could not borrow sale collection capability from provided sale collection address")
552
553            saleCollectionCapability.listForTransfer(tokenID: tokenID, salePrice: salePrice, auctionPrice: auctionPrice, auctionStartTimestamp: auctionStartTimestamp, auctionPeriodSeconds: auctionPeriodSeconds)
554        }
555
556        access(all) fun listForRent(
557            saleCollectionAddress: Address, 
558            tokenID: UInt64, 
559            price: UFix64?,
560            rentalPeriodSeconds: UFix64?
561        ) {
562            let saleCollectionCapability = getAccount(saleCollectionAddress).capabilities.borrow<&TrmMarketV2_2.SaleCollection>(
563                TrmMarketV2_2.marketPublicPath
564                )
565            ?? panic("Could not borrow sale collection capability from provided sale collection address")
566
567            saleCollectionCapability.listForRent(tokenID: tokenID, price: price, rentalPeriodSeconds: rentalPeriodSeconds)
568        }
569
570        access(all) fun batchListForTransfer(
571            saleCollectionAddress: Address, 
572            tokenIDs: [UInt64], 
573            salePrice: UFix64?, 
574            auctionPrice: UFix64?,
575            auctionStartTimestamp: UFix64?,
576            auctionPeriodSeconds: UFix64?
577        ) {
578            let saleCollectionCapability = getAccount(saleCollectionAddress).capabilities.borrow<&TrmMarketV2_2.SaleCollection>(
579                TrmMarketV2_2.marketPublicPath
580                )
581            ?? panic("Could not borrow sale collection capability from provided sale collection address")
582
583            saleCollectionCapability.batchListForTransfer(tokenIDs: tokenIDs, salePrice: salePrice, auctionPrice: auctionPrice, auctionStartTimestamp: auctionStartTimestamp, auctionPeriodSeconds: auctionPeriodSeconds)
584        }
585
586        access(all) fun batchListForRent(
587            saleCollectionAddress: Address, 
588            tokenIDs: [UInt64], 
589            price: UFix64?,
590            rentalPeriodSeconds: UFix64?
591        ) {
592            let saleCollectionCapability = getAccount(saleCollectionAddress).capabilities.borrow<&TrmMarketV2_2.SaleCollection>(
593                TrmMarketV2_2.marketPublicPath
594                )
595            ?? panic("Could not borrow sale collection capability from provided sale collection address")
596
597            saleCollectionCapability.batchListForRent(tokenIDs: tokenIDs, price: price, rentalPeriodSeconds: rentalPeriodSeconds)
598        }
599
600        access(all) fun cancelTransfer(
601            saleCollectionAddress: Address, 
602            tokenID: UInt64
603        ) {
604            let saleCollectionCapability = getAccount(saleCollectionAddress).capabilities.borrow<&TrmMarketV2_2.SaleCollection>(
605                TrmMarketV2_2.marketPublicPath
606                )
607            ?? panic("Could not borrow sale collection capability from provided sale collection address")
608            
609            saleCollectionCapability.cancelTransfer(tokenID: tokenID)
610        }
611
612        access(all) fun cancelRent(
613            saleCollectionAddress: Address, 
614            tokenID: UInt64
615        ) {
616            let saleCollectionCapability = getAccount(saleCollectionAddress).capabilities.borrow<&TrmMarketV2_2.SaleCollection>(
617                TrmMarketV2_2.marketPublicPath
618                )
619            ?? panic("Could not borrow sale collection capability from provided sale collection address")
620            
621            saleCollectionCapability.cancelRent(tokenID: tokenID)
622        }
623
624        access(all) fun batchCancelTransfer(
625            saleCollectionAddress: Address, 
626            tokenIDs: [UInt64]
627        ) {
628            let saleCollectionCapability = getAccount(saleCollectionAddress).capabilities.borrow<&TrmMarketV2_2.SaleCollection>(
629                TrmMarketV2_2.marketPublicPath
630                )
631            ?? panic("Could not borrow sale collection capability from provided sale collection address")
632            
633            saleCollectionCapability.batchCancelTransfer(tokenIDs: tokenIDs)
634        }
635
636        access(all) fun batchCancelRent(
637            saleCollectionAddress: Address, 
638            tokenIDs: [UInt64]
639        ) {
640            let saleCollectionCapability = getAccount(saleCollectionAddress).capabilities.borrow<&TrmMarketV2_2.SaleCollection>(
641                TrmMarketV2_2.marketPublicPath
642                )
643            ?? panic("Could not borrow sale collection capability from provided sale collection address")
644            
645            saleCollectionCapability.batchCancelRent(tokenIDs: tokenIDs)
646        }
647
648        access(all) fun transfer(
649            saleCollectionAddress: Address, 
650            tokenID: UInt64,
651            assetAdminResource: &TrmAssetV2_2.Admin,
652            recipientAddress: Address,
653            price: UFix64,
654            paymentID: String
655        ) {
656            let saleCollectionCapability = getAccount(saleCollectionAddress).capabilities.borrow<&TrmMarketV2_2.SaleCollection>(
657                TrmMarketV2_2.marketPublicPath
658                )
659            ?? panic("Could not borrow sale collection capability from provided sale collection address")
660            
661            saleCollectionCapability.transfer(tokenID: tokenID, adminAccount: assetAdminResource, recipientAddress: recipientAddress, price: price, paymentID: paymentID)
662        }
663
664        access(all) fun rent(
665            saleCollectionAddress: Address, 
666            tokenID: UInt64,
667            price: UFix64,
668            recipient: Capability<&TrmRentV2_2.Collection>,
669            paymentID: String
670        ): UInt64 {
671            let saleCollectionCapability = getAccount(saleCollectionAddress).capabilities.borrow<&TrmMarketV2_2.SaleCollection>(
672                TrmMarketV2_2.marketPublicPath
673                )
674            ?? panic("Could not borrow sale collection capability from provided sale collection address")
675            
676            return saleCollectionCapability.rent(tokenID: tokenID, price: price, recipient: recipient, paymentID: paymentID)
677        }
678    }
679
680    init() {
681        // Settings paths
682        self.marketStoragePath = /storage/TrmMarketV2_2SaleCollection
683        self.marketPublicPath = /public/TrmMarketV2_2SaleCollection
684        self.adminStoragePath = /storage/TrmMarketV2_2Admin
685
686        /// First, check to see if a admin resource already exists
687        if self.account.storage.type(at: self.adminStoragePath) == nil {
688        
689            /// Put the Admin in storage
690            self.account.storage.save<@Admin>(<- create Admin(), to: self.adminStoragePath)
691        }
692
693        /// obtain Admin's private rent minter capability
694        //self.minterCapability = self.account.getCapabilities<&TrmRentV2_2.Minter>(TrmRentV2_2.minterPrivatePath)
695       //self.minterCapability = self.account.storage.copy<Capability<&TrmRentV2_2.Minter>>(from: TrmRentV2_2.minterStoragePath)!
696
697        emit ContractInitialized()
698    }
699}
700