Smart Contract

TrmMarketV2_1_1

A.61fc4b873e58733b.TrmMarketV2_1_1

Deployed

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

Dependents

0 imports
1/**
2
3    TrmMarketV2_1_1.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_1.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_1 from 0x61fc4b873e58733b
43import TrmRentV2_1 from 0x61fc4b873e58733b
44import NonFungibleToken from 0x1d7e57aa55817448
45
46pub contract TrmMarketV2_1_1 {
47    /// -----------------------------------------------------------------------
48    /// TRM Market contract Event definitions
49    /// -----------------------------------------------------------------------
50
51    /// Event that emitted when the NFT contract is initialized
52    pub event ContractInitialized()
53    /// Emitted when an Asset is listed for transfer
54    pub event AssetListedForTransfer(assetTokenID: UInt64, salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?, seller: Address?)
55    /// Emitted when an Asset is listed for transfer
56    pub event AssetBatchListedForTransfer(assetTokenIDs: [UInt64], salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?, seller: Address?)
57    /// Emitted when an Asset is listed for rent
58    pub event AssetListedForRent(assetTokenID: UInt64, price: UFix64?, rentalPeriodSeconds: UFix64, seller: Address?)
59    /// Emitted when an Asset is listed for rent
60    pub event AssetBatchListedForRent(assetTokenIDs: [UInt64], price: UFix64?, rentalPeriodSeconds: UFix64, seller: Address?)
61    /// Emitted when the sale price of a listed asset has changed
62    pub event AssetTransferListingChanged(assetTokenID: UInt64, salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?, seller: Address?)
63    /// Emitted when the sale price of a listed asset has changed
64    pub event AssetBatchTransferListingChanged(assetTokenIDs: [UInt64], salePrice: UFix64?, auctionPrice: UFix64?, auctionStartTimestamp: UFix64?, auctionPeriodSeconds: UFix64?, seller: Address?)
65    /// Emitted when the rent price of a listed asset has changed
66    pub event AssetRentListingChanged(assetTokenID: UInt64, price: UFix64?, rentalPeriodSeconds: UFix64, seller: Address?)
67    /// Emitted when the rent price of a listed asset has changed
68    pub event AssetBatchRentListingChanged(assetTokenIDs: [UInt64], price: UFix64?, rentalPeriodSeconds: UFix64, seller: Address?)
69    /// Emitted when a token is purchased from the market
70    pub event AssetTransferred(assetTokenID: UInt64, price: UFix64, seller: Address?, buyer: Address?, paymentID: String)
71    /// Emitted when a token is rented from the market
72    pub 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)
73    /// Emitted when a token has been delisted for sale
74    pub event AssetDelistedForTransfer(assetTokenID: UInt64, owner: Address?)
75    /// Emitted when a token has been delisted for sale
76    pub event AssetBatchDelistedForTransfer(assetTokenIDs: [UInt64], owner: Address?)
77    /// Emitted when a token has been delisted for rent
78    pub event AssetDelistedForRent(assetTokenID: UInt64, owner: Address?)
79    /// Emitted when a token has been delisted for rent
80    pub event AssetBatchDelistedForRent(assetTokenIDs: [UInt64], owner: Address?)
81
82    /// Path where the `SaleCollection` is stored
83    pub let marketStoragePath: StoragePath
84    /// Path where the public capability for the `SaleCollection` is
85    pub let marketPublicPath: PublicPath
86    /// Path where the private capability for the `SaleCollection` is
87    pub let marketPrivatePath: PrivatePath
88    /// Path where the 'Admin' resource is stored
89    pub let adminStoragePath: StoragePath
90
91    /// The private capability for minting rent tokens
92    pub var minterCapability: Capability<&TrmRentV2_1.Minter>
93
94    pub resource RentListing {
95        pub var price: UFix64
96        pub var rentalPeriodSeconds: UFix64
97
98        access(contract) fun setRentalPeriodSeconds(seconds: UFix64) {
99            self.rentalPeriodSeconds = seconds
100        }
101
102        init(rentalPeriodSeconds: UFix64) {
103            self.price = UFix64(0)
104            self.rentalPeriodSeconds = rentalPeriodSeconds
105        }
106    }
107
108    pub resource TransferListing {
109        pub var price: UFix64
110
111        init() {
112            self.price = UFix64(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    pub 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_1.Admin, recipientAddress: Address, price: UFix64, paymentID: String)
130        access(contract) fun rent(tokenID: UInt64, price: UFix64, recipient: Capability<&{TrmRentV2_1.CollectionPublic}>, paymentID: String): UInt64
131
132        pub fun getTransferListing(tokenID: UInt64): &TransferListing?
133        pub fun getRentListing(tokenID: UInt64): &RentListing?
134        pub fun getTransferIDs(): [UInt64]
135        pub fun getRentIDs(): [UInt64]
136        pub fun borrowAsset(id: UInt64): &TrmAssetV2_1.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    pub resource SaleCollection: SalePublic {
158
159        /// A reference to collection of the user's assets 
160        access(self) var ownerCollection: Capability<&TrmAssetV2_1.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_1.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_1.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_1.CollectionPublic}>, 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_1_1.minterCapability.borrow()!
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        pub 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        pub 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        pub fun getTransferIDs(): [UInt64] {
502            return self.transferListings.keys
503        }
504
505        /// getRentIDs returns an array of token IDs that are for sale
506        pub 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_1.NFT? Optional reference to a token for transfer so that the caller can read its data
515        pub fun borrowAsset(id: UInt64): &TrmAssetV2_1.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    pub fun createSaleCollection(ownerCollection: Capability<&TrmAssetV2_1.Collection>): @SaleCollection {
527
528        return <- create SaleCollection(ownerCollection: ownerCollection)
529    }
530
531    /// Admin is a special authorization resource that 
532    /// allows the admin to perform important functions
533    pub resource Admin {
534
535        pub fun listForTransfer(
536            saleCollectionAddress: Address, 
537            tokenID: UInt64, 
538            salePrice: UFix64?, 
539            auctionPrice: UFix64?,
540            auctionStartTimestamp: UFix64?,
541            auctionPeriodSeconds: UFix64?
542        ) {
543            let saleCollectionCapability = getAccount(saleCollectionAddress).getCapability<&TrmMarketV2_1_1.SaleCollection{TrmMarketV2_1_1.SalePublic}>(
544                TrmMarketV2_1_1.marketPublicPath
545                ).borrow()
546            ?? panic("Could not borrow sale collection capability from provided sale collection address")
547
548            saleCollectionCapability.listForTransfer(tokenID: tokenID, salePrice: salePrice, auctionPrice: auctionPrice, auctionStartTimestamp: auctionStartTimestamp, auctionPeriodSeconds: auctionPeriodSeconds)
549        }
550
551        pub fun listForRent(
552            saleCollectionAddress: Address, 
553            tokenID: UInt64, 
554            price: UFix64?,
555            rentalPeriodSeconds: UFix64?
556        ) {
557            let saleCollectionCapability = getAccount(saleCollectionAddress).getCapability<&TrmMarketV2_1_1.SaleCollection{TrmMarketV2_1_1.SalePublic}>(
558                TrmMarketV2_1_1.marketPublicPath
559                ).borrow()
560            ?? panic("Could not borrow sale collection capability from provided sale collection address")
561
562            saleCollectionCapability.listForRent(tokenID: tokenID, price: price, rentalPeriodSeconds: rentalPeriodSeconds)
563        }
564
565        pub fun batchListForTransfer(
566            saleCollectionAddress: Address, 
567            tokenIDs: [UInt64], 
568            salePrice: UFix64?, 
569            auctionPrice: UFix64?,
570            auctionStartTimestamp: UFix64?,
571            auctionPeriodSeconds: UFix64?
572        ) {
573            let saleCollectionCapability = getAccount(saleCollectionAddress).getCapability<&TrmMarketV2_1_1.SaleCollection{TrmMarketV2_1_1.SalePublic}>(
574                TrmMarketV2_1_1.marketPublicPath
575                ).borrow()
576            ?? panic("Could not borrow sale collection capability from provided sale collection address")
577
578            saleCollectionCapability.batchListForTransfer(tokenIDs: tokenIDs, salePrice: salePrice, auctionPrice: auctionPrice, auctionStartTimestamp: auctionStartTimestamp, auctionPeriodSeconds: auctionPeriodSeconds)
579        }
580
581        pub fun batchListForRent(
582            saleCollectionAddress: Address, 
583            tokenIDs: [UInt64], 
584            price: UFix64?,
585            rentalPeriodSeconds: UFix64?
586        ) {
587            let saleCollectionCapability = getAccount(saleCollectionAddress).getCapability<&TrmMarketV2_1_1.SaleCollection{TrmMarketV2_1_1.SalePublic}>(
588                TrmMarketV2_1_1.marketPublicPath
589                ).borrow()
590            ?? panic("Could not borrow sale collection capability from provided sale collection address")
591
592            saleCollectionCapability.batchListForRent(tokenIDs: tokenIDs, price: price, rentalPeriodSeconds: rentalPeriodSeconds)
593        }
594
595        pub fun cancelTransfer(
596            saleCollectionAddress: Address, 
597            tokenID: UInt64
598        ) {
599            let saleCollectionCapability = getAccount(saleCollectionAddress).getCapability<&TrmMarketV2_1_1.SaleCollection{TrmMarketV2_1_1.SalePublic}>(
600                TrmMarketV2_1_1.marketPublicPath
601                ).borrow()
602            ?? panic("Could not borrow sale collection capability from provided sale collection address")
603            
604            saleCollectionCapability.cancelTransfer(tokenID: tokenID)
605        }
606
607        pub fun cancelRent(
608            saleCollectionAddress: Address, 
609            tokenID: UInt64
610        ) {
611            let saleCollectionCapability = getAccount(saleCollectionAddress).getCapability<&TrmMarketV2_1_1.SaleCollection{TrmMarketV2_1_1.SalePublic}>(
612                TrmMarketV2_1_1.marketPublicPath
613                ).borrow()
614            ?? panic("Could not borrow sale collection capability from provided sale collection address")
615            
616            saleCollectionCapability.cancelRent(tokenID: tokenID)
617        }
618
619        pub fun batchCancelTransfer(
620            saleCollectionAddress: Address, 
621            tokenIDs: [UInt64]
622        ) {
623            let saleCollectionCapability = getAccount(saleCollectionAddress).getCapability<&TrmMarketV2_1_1.SaleCollection{TrmMarketV2_1_1.SalePublic}>(
624                TrmMarketV2_1_1.marketPublicPath
625                ).borrow()
626            ?? panic("Could not borrow sale collection capability from provided sale collection address")
627            
628            saleCollectionCapability.batchCancelTransfer(tokenIDs: tokenIDs)
629        }
630
631        pub fun batchCancelRent(
632            saleCollectionAddress: Address, 
633            tokenIDs: [UInt64]
634        ) {
635            let saleCollectionCapability = getAccount(saleCollectionAddress).getCapability<&TrmMarketV2_1_1.SaleCollection{TrmMarketV2_1_1.SalePublic}>(
636                TrmMarketV2_1_1.marketPublicPath
637                ).borrow()
638            ?? panic("Could not borrow sale collection capability from provided sale collection address")
639            
640            saleCollectionCapability.batchCancelRent(tokenIDs: tokenIDs)
641        }
642
643        pub fun transfer(
644            saleCollectionAddress: Address, 
645            tokenID: UInt64,
646            assetAdminResource: &TrmAssetV2_1.Admin,
647            recipientAddress: Address,
648            price: UFix64,
649            paymentID: String
650        ) {
651            let saleCollectionCapability = getAccount(saleCollectionAddress).getCapability<&TrmMarketV2_1_1.SaleCollection{TrmMarketV2_1_1.SalePublic}>(
652                TrmMarketV2_1_1.marketPublicPath
653                ).borrow()
654            ?? panic("Could not borrow sale collection capability from provided sale collection address")
655            
656            saleCollectionCapability.transfer(tokenID: tokenID, adminAccount: assetAdminResource, recipientAddress: recipientAddress, price: price, paymentID: paymentID)
657        }
658
659        pub fun rent(
660            saleCollectionAddress: Address, 
661            tokenID: UInt64,
662            price: UFix64,
663            recipient: Capability<&{TrmRentV2_1.CollectionPublic}>,
664            paymentID: String
665        ): UInt64 {
666            let saleCollectionCapability = getAccount(saleCollectionAddress).getCapability<&TrmMarketV2_1_1.SaleCollection{TrmMarketV2_1_1.SalePublic}>(
667                TrmMarketV2_1_1.marketPublicPath
668                ).borrow()
669            ?? panic("Could not borrow sale collection capability from provided sale collection address")
670            
671            return saleCollectionCapability.rent(tokenID: tokenID, price: price, recipient: recipient, paymentID: paymentID)
672        }
673    }
674
675    init() {
676        // Settings paths
677        self.marketStoragePath = /storage/TrmMarketV2_1_1SaleCollection
678        self.marketPublicPath = /public/TrmMarketV2_1_1SaleCollection
679        self.marketPrivatePath = /private/TrmMarketV2_1_1SaleCollection
680        self.adminStoragePath = /storage/TrmMarketV2_1_1Admin
681
682        /// First, check to see if a admin resource already exists
683        if self.account.type(at: self.adminStoragePath) == nil {
684        
685            /// Put the Admin in storage
686            self.account.save<@Admin>(<- create Admin(), to: self.adminStoragePath)
687        }
688
689        /// obtain Admin's private rent minter capability
690        self.minterCapability = self.account.getCapability<&TrmRentV2_1.Minter>(TrmRentV2_1.minterPrivatePath)
691
692        emit ContractInitialized()
693    }
694}
695