Smart Contract

TrmRentV2_1

A.61fc4b873e58733b.TrmRentV2_1

Deployed

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

Dependents

0 imports
1/**
2
3    TrmRentV2_1.cdc
4
5    Description: Contract definitions for initializing Rent Collections, Rent NFT Resource and Rent Minter
6
7    Rent contract is used for defining the Rent NFT, Rent Collection,
8    Rent Collection Public Interface and Rent Minter
9
10    ## `NFT` resource
11
12    The core resource type that represents an Rent NFT in the smart contract.
13
14    ## `Collection` Resource
15
16    The resource that stores a user's Rent NFT collection.
17    It includes a few functions to allow the owner to easily
18    move tokens in and out of the collection.
19
20    ## `Receiver` resource interfaces
21
22    This interface is used for depositing Rent NFTs to the Rent Collectio.
23    It also exposes few functions to fetch data about Rent token
24
25    To send an NFT to another user, a user would simply withdraw the NFT
26    from their Collection, then call the deposit function on another user's
27    Collection to complete the transfer.
28
29    ## `Minter` Resource
30
31    Minter resource is used for minting Rent NFTs
32
33*/
34import NonFungibleToken from 0x1d7e57aa55817448
35import MetadataViews from 0x61fc4b873e58733b
36
37pub contract TrmRentV2_1: NonFungibleToken {
38    // -----------------------------------------------------------------------
39    // Rent contract Event definitions
40    // -----------------------------------------------------------------------
41
42    // The total number of tokens of this type in existence
43    pub var totalSupply: UInt64
44    // Event that emitted when the NFT contract is initialized
45    pub event ContractInitialized()
46    // Emitted when a Rent is minted
47    pub event RentMinted(id: UInt64, assetTokenID: UInt64, kID: String, assetURL: String, expiryTimestamp: UFix64)
48    // Emitted when a Rent is destroyed
49    pub event RentDestroyed(id: UInt64)
50    // Event that is emitted when a token is withdrawn, indicating the owner of the collection that it was withdrawn from.
51    pub event Withdraw(id: UInt64, from: Address?)
52    // Event that emitted when a token is deposited to a collection. It indicates the owner of the collection that it was deposited to.
53    pub event Deposit(id: UInt64, to: Address?)
54
55    /// Paths where Storage and capabilities are stored
56    pub let collectionStoragePath: StoragePath
57    pub let collectionPublicPath: PublicPath
58    pub let minterStoragePath: StoragePath
59    pub let minterPrivatePath: PrivatePath
60
61    // RentData
62    //
63    // Struct for storing metadata for Rent
64    pub struct RentData {
65        pub let assetTokenID: UInt64
66        pub let kID: String
67        pub var assetName: String
68        pub var assetDescription: String
69        pub let assetURL: String
70        pub var assetThumbnailURL: String
71        pub var assetMetadata: {String: String}
72        pub var rentMetadata: {String: String}
73        pub let expiryTimestamp: UFix64
74
75        init(assetTokenID: UInt64, kID: String, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetMetadata: {String: String}, expiryTimestamp: UFix64) {
76            // pre {
77            //     expiryTimestamp > getCurrentBlock().timestamp:
78            //         "Expiry timestamp should be greater than current timestamp"
79            // }
80            self.assetTokenID = assetTokenID
81            self.kID = kID
82            self.assetName = assetName
83            self.assetDescription = assetDescription
84            self.assetURL = assetURL
85            self.assetThumbnailURL = assetThumbnailURL
86            self.assetMetadata = assetMetadata
87            self.assetMetadata = {}
88            self.rentMetadata = {}
89            self.expiryTimestamp = expiryTimestamp
90        }
91    }
92
93    //  NFT
94    //
95    // The main Rent NFT resource that signifies that an asset is rented by a user
96    pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
97        pub let id: UInt64
98        pub let data: RentData
99
100        pub fun getViews(): [Type] {
101            return [
102                Type<MetadataViews.Display>()
103            ]
104        }
105
106        pub fun resolveView(_ view: Type): AnyStruct? {
107            switch view {
108                case Type<MetadataViews.Display>():
109                    return MetadataViews.Display(
110                        name: self.data.assetName.concat("(Rented)"),
111                        description: self.data.assetDescription,
112                        thumbnail: MetadataViews.HTTPFile(
113                            url: self.data.assetThumbnailURL
114                        )
115                    )
116            }
117
118            return nil
119        }
120
121        init(id: UInt64, assetTokenID: UInt64, kID: String, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetMetadata: {String: String}, expiryTimestamp: UFix64) {
122            self.id = id
123            self.data = RentData(assetTokenID: assetTokenID, kID: kID, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetMetadata: assetMetadata, expiryTimestamp: expiryTimestamp)
124
125            emit RentMinted(id: self.id, assetTokenID: assetTokenID, kID: kID, assetURL: assetURL, expiryTimestamp: expiryTimestamp)
126        }
127
128        // If the Rent NFT is destroyed, emit an event to indicate to outside ovbservers that it has been destroyed
129        destroy() {
130            emit RentDestroyed(id: self.id)
131        }
132    }
133
134    // CollectionPublic
135    //
136    // Public interface for Rent Collection
137    // This exposes functions for depositing NFTs
138    // and also for returning some info for a specific
139    // Rent NFT id and Asset NFT id
140    pub resource interface CollectionPublic {
141        access(contract) fun depositRent(token: @NonFungibleToken.NFT)
142
143        pub fun getIDs(): [UInt64]
144        pub fun getAssetTokenIDs(): [UInt64]
145        pub fun getID(assetTokenID: UInt64): UInt64
146        pub fun getAssetTokenID(id: UInt64): UInt64
147        pub fun idExists(id: UInt64): Bool
148        pub fun assetTokenIDExists(assetTokenID: UInt64): Bool
149        pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
150        pub fun borrowNFTUsingAssetTokenID(assetTokenID: UInt64): &NonFungibleToken.NFT?
151        pub fun borrowRent(id: UInt64): &NFT
152        pub fun borrowRentUsingAssetTokenID(assetTokenID: UInt64): &NFT
153        pub fun getExpiryTimestamp(id: UInt64): UFix64
154        pub fun getExpiryTimestampUsingAssetTokenID(assetTokenID: UInt64): UFix64
155        pub fun isExpired(id: UInt64): Bool
156        pub fun isExpiredUsingAssetTokenID(assetTokenID: UInt64): Bool
157        pub fun isRentValid(id: UInt64): Bool
158        pub fun isRentValidUsingAssetTokenID(assetTokenID: UInt64): Bool
159    }
160
161    // Collection
162    //
163    // The resource that stores a user's Rent NFT collection.
164    // It includes a few functions to allow the owner to easily
165    // moveto deposit or withdraw the tokens.
166    pub resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection, CollectionPublic {
167        
168        // Dictionary to hold the Rent NFTs in the Collection
169        pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
170        // Dictionary mapping Asset Token IDs to Rent Token IDs
171        pub var rentedNFTs: {UInt64: UInt64}
172
173        init() {
174            self.ownedNFTs <- {}
175            self.rentedNFTs = {}
176        }
177
178        // withdraw removes an NFT from the collection and moves it to the caller
179        pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
180            pre {
181                false: "Withdrawing Rent directly from Rent contract is not allowed"
182            }
183            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Rent Token ID does not exist")
184            let rentToken <- token as! @NFT
185            self.rentedNFTs.remove(key: rentToken.data.assetTokenID)
186            emit Withdraw(id: withdrawID, from: self.owner!.address)
187            return <- rentToken
188        }
189
190        // deposit takes an NFT as an argument and adds it to the Collection
191        pub fun deposit(token: @NonFungibleToken.NFT) {
192            pre {
193                false: "Depositing Rent directly to Rent contract is not allowed"
194            }
195
196            let rentToken <- token as! @NFT
197
198            if self.isRentValidUsingAssetTokenID(assetTokenID: rentToken.data.assetTokenID) {
199                destroy rentToken
200                panic("Valid Rent token for this asset already exists")
201            } else {
202                if let existingRentTokenID = self.rentedNFTs[rentToken.data.assetTokenID] {
203                    let existingRentToken <- self.withdrawRent(id: existingRentTokenID)
204                    destroy existingRentToken
205                }
206                let newRentTokenId = rentToken.id
207                self.rentedNFTs[rentToken.data.assetTokenID] = newRentTokenId
208                let oldToken <- self.ownedNFTs[newRentTokenId] <- rentToken
209                destroy oldToken
210                emit Deposit(id: newRentTokenId, to: self.owner!.address)
211            }
212        }
213
214        // getIDs returns an array of the IDs that are in the collection
215        pub fun getIDs(): [UInt64] {
216            return self.ownedNFTs.keys
217        }
218
219        // borrowViewResolver is to conform with MetadataViews
220        pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
221            let refNFT = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
222            let refRentNFT = refNFT as! &NFT
223
224            return refRentNFT as &AnyResource{MetadataViews.Resolver}
225        }
226
227        // getAssetTokenIDs returns an array of the asset token IDs that are in the collection
228        pub fun getAssetTokenIDs(): [UInt64] {
229            return self.rentedNFTs.keys
230        }
231
232        // Returns the rent token ID for an NFT from assetTokenID in the collection
233        pub fun getID(assetTokenID: UInt64): UInt64 {
234            return self.rentedNFTs[assetTokenID] ?? panic("Asset Token ID does not exist")
235        }
236
237        // Returns the asset token ID for an NFT in the collection
238        pub fun getAssetTokenID(id: UInt64): UInt64 {
239            pre {
240                self.ownedNFTs[id] != nil: "Rent Token ID does not exist"
241            }
242            let refNFT = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
243            let refRentNFT = refNFT as! &NFT
244            
245            return refRentNFT.data.assetTokenID
246        }
247
248        // Checks if id of NFT exists in collection
249        pub fun idExists(id: UInt64): Bool {
250            return self.ownedNFTs[id] != nil
251        }
252
253        // Checks if id of NFT exists in collection
254        pub fun assetTokenIDExists(assetTokenID: UInt64): Bool {
255            if (self.rentedNFTs[assetTokenID] == nil) {
256                return false
257            }
258            let rentTokenID = self.rentedNFTs[assetTokenID] ?? panic("Rent Token ID does not exist")
259            return self.ownedNFTs[rentTokenID] != nil
260        }
261
262        // Returns a borrowed reference to an NFT in the collection so that the caller can read data and call methods from it
263        pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
264            return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)!
265        }
266
267        // Returns a borrowed reference to an NFT in the collection using asset token id so that the caller can read data and call methods from it
268        pub fun borrowNFTUsingAssetTokenID(assetTokenID: UInt64): &NonFungibleToken.NFT? {
269            let rentTokenID = self.rentedNFTs[assetTokenID] ?? panic("Rent Token ID does not exist")
270            return &self.ownedNFTs[rentTokenID] as &NonFungibleToken.NFT?
271        }
272
273        // Returns a borrowed reference to the Rent NFT in the collection so that the caller can read data and call methods from it
274        pub fun borrowRent(id: UInt64): &NFT {
275            pre {
276                self.ownedNFTs[id] != nil: "Rent Token ID does not exist"
277            }
278            let refNFT = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
279            return refNFT as! &NFT
280        }
281
282        // Returns a borrowed reference to the RENT NFT in the collection using asset token id so that the caller can read data and call methods from it
283        pub fun borrowRentUsingAssetTokenID(assetTokenID: UInt64): &NFT {
284            pre {
285                self.rentedNFTs[assetTokenID] != nil: "Asset Token ID does not exist"
286                self.ownedNFTs[self.rentedNFTs[assetTokenID]!] != nil: "Rent Token ID does not exist"
287            }
288            let rentTokenID = self.rentedNFTs[assetTokenID]!
289            let refNFT = (&self.ownedNFTs[rentTokenID] as auth &NonFungibleToken.NFT?)!
290            return refNFT as! &NFT
291            
292        }
293
294        // Returns the expiry for an NFT in the collection
295        pub fun getExpiryTimestamp(id: UInt64): UFix64 {
296            pre {
297                self.ownedNFTs[id] != nil: "Rent Token ID does not exist"
298            }
299            let refNFT = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
300            let refRentNFT = refNFT as! &NFT
301            return refRentNFT.data.expiryTimestamp
302        }
303
304        // Returns the expiry for an NFT in the collection using asset token id
305        pub fun getExpiryTimestampUsingAssetTokenID(assetTokenID: UInt64): UFix64 {
306            pre {
307                self.rentedNFTs[assetTokenID] != nil: "Asset Token ID does not exist"
308                self.ownedNFTs[self.rentedNFTs[assetTokenID]!] != nil: "Rent Token ID does not exist"
309            }
310            let rentTokenID = self.rentedNFTs[assetTokenID]!
311
312            let refNFT = (&self.ownedNFTs[rentTokenID] as auth &NonFungibleToken.NFT?)!
313            let refRentNFT = refNFT as! &NFT
314            return refRentNFT.data.expiryTimestamp
315        }
316
317        // Returns if token is expired
318        pub fun isExpired(id: UInt64): Bool {
319            pre {
320                self.ownedNFTs[id] != nil: "Rent Token ID does not exist"
321            }
322            let refNFT = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
323            let refRentNFT = refNFT as! &NFT
324            return refRentNFT.data.expiryTimestamp < getCurrentBlock().timestamp
325        }
326
327        // Returns if token is expired using asset token id
328        pub fun isExpiredUsingAssetTokenID(assetTokenID: UInt64): Bool {
329            pre {
330                self.rentedNFTs[assetTokenID] != nil: "Asset Token ID does not exist"
331                self.ownedNFTs[self.rentedNFTs[assetTokenID]!] != nil: "Rent Token ID does not exist"
332            }
333            let rentTokenID = self.rentedNFTs[assetTokenID]!
334
335            let refNFT = (&self.ownedNFTs[rentTokenID] as auth &NonFungibleToken.NFT?)!
336            let refRentNFT = refNFT as! &NFT
337            return refRentNFT.data.expiryTimestamp < getCurrentBlock().timestamp
338        }
339
340        // Returns if rent is valid for rent id
341        pub fun isRentValid(id: UInt64): Bool {
342            if self.ownedNFTs[id] != nil {
343
344                let refNFT = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
345                let refRentNFT = refNFT as! &NFT
346                return refRentNFT.data.expiryTimestamp > getCurrentBlock().timestamp
347
348            } else {
349                return false
350            }
351        }
352
353        // Returns if rent is valid for asset token id
354        pub fun isRentValidUsingAssetTokenID(assetTokenID: UInt64): Bool {
355            if let rentTokenID = self.rentedNFTs[assetTokenID] {
356
357                if self.ownedNFTs[rentTokenID] != nil {
358
359                    let refNFT = (&self.ownedNFTs[rentTokenID] as auth &NonFungibleToken.NFT?)!
360                    let refRentNFT = refNFT as! &NFT
361                    return refRentNFT.data.expiryTimestamp > getCurrentBlock().timestamp
362                    
363                } else {
364                    return false
365                }
366            }
367            return false
368        }
369
370        // Destroys specified token in the collection
371        access(contract) fun destroyToken(id: UInt64) {
372            pre {
373                self.ownedNFTs[id] != nil: "Rent Token ID does not exist"
374            }
375            self.rentedNFTs.remove(key: self.getAssetTokenID(id: id))
376            let oldToken <- self.ownedNFTs.remove(key: id) 
377            destroy oldToken
378        }
379
380        // withdraw removes an NFT from the collection and moves it to the caller
381        access(contract) fun withdrawRent(id: UInt64): @NonFungibleToken.NFT {
382            let token <- self.ownedNFTs.remove(key: id) ?? panic("Rent Token ID does not exist")
383            let rentToken <- token as! @NFT
384            self.rentedNFTs.remove(key: rentToken.data.assetTokenID)
385            emit Withdraw(id: id, from: self.owner!.address)
386            return <- rentToken
387        }
388
389        // deposit takes an NFT as an argument and adds it to the Collection
390        access(contract) fun depositRent(token: @NonFungibleToken.NFT) {
391            let rentToken <- token as! @NFT
392
393            if self.isRentValidUsingAssetTokenID(assetTokenID: rentToken.data.assetTokenID) {
394                destroy rentToken
395                panic("Valid Rent token for this asset already exists")
396            } else {
397                if let existingRentTokenID = self.rentedNFTs[rentToken.data.assetTokenID] {
398                    let existingRentToken <- self.withdrawRent(id: existingRentTokenID)
399                    destroy existingRentToken
400                }
401                let newRentTokenId = rentToken.id
402                self.rentedNFTs[rentToken.data.assetTokenID] = newRentTokenId
403                let oldToken <- self.ownedNFTs[newRentTokenId] <- rentToken
404                destroy oldToken
405                emit Deposit(id: newRentTokenId, to: self.owner!.address)
406            }
407        }
408
409        // If a transaction destroys the Collection object, all the NFTs contained within are also destroyed!
410        destroy() {
411            destroy self.ownedNFTs
412        }
413    }
414
415    // createEmptyCollection creates an empty Collection and returns it to the caller so that they can own NFTs
416    pub fun createEmptyCollection(): @Collection {
417        return <- create Collection()
418    }
419
420    // Minter
421    //
422    // Minter is a special resource that is used for minting Rent Tokens
423    pub resource Minter {
424
425        // mintNFT mints the rent NFT and stores it in the collection of recipient
426        pub fun mintNFT(assetTokenID: UInt64, kID: String, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetMetadata: {String: String}, expiryTimestamp: UFix64, recipient: &AnyResource{CollectionPublic}): UInt64 {
427            // pre {
428            //     expiryTimestamp > getCurrentBlock().timestamp:
429            //         "Expiry timestamp should be greater than current timestamp"
430            // }
431            
432            let id = TrmRentV2_1.totalSupply
433            TrmRentV2_1.totalSupply = id + 1
434
435            recipient.depositRent(token: <- create NFT(id: id, assetTokenID: assetTokenID, kID: kID, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetMetadata: assetMetadata, expiryTimestamp: expiryTimestamp))
436            
437            return id
438        }
439    }
440
441    init() {
442        self.totalSupply = 0
443
444        // Settings paths
445        self.collectionStoragePath = /storage/TrmRentV2_1Collection
446        self.collectionPublicPath = /public/TrmRentV2_1Collection
447        self.minterStoragePath = /storage/TrmRentV2_1Minter
448        self.minterPrivatePath = /private/TrmRentV2_1Minter
449
450        // First, check to see if a minter resource already exists
451        if self.account.type(at: self.minterStoragePath) == nil {
452
453            // Put the minter in storage with access only to admin
454            self.account.save(<-create Minter(), to: self.minterStoragePath)
455        }
456
457        self.account.link<&TrmRentV2_1.Minter>(self.minterPrivatePath, target: TrmRentV2_1.minterStoragePath)
458
459        emit ContractInitialized()
460    }
461}