Smart Contract

TrmRentV2_2

A.61fc4b873e58733b.TrmRentV2_2

Deployed

1w ago
Feb 16, 2026, 02:02:31 AM UTC

Dependents

21 imports
1/**
2
3    TrmRentV2_2.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 0x1d7e57aa55817448
36import ViewResolver from 0x1d7e57aa55817448
37
38access(all) contract TrmRentV2_2: NonFungibleToken {
39    // -----------------------------------------------------------------------
40    // Rent contract Event definitions
41    // -----------------------------------------------------------------------
42
43    // The total number of tokens of this type in existence
44    access(all) var totalSupply: UInt64
45    // Event that emitted when the NFT contract is initialized
46    access(all) event ContractInitialized()
47    // Event that emitted when the rent collection is initialized
48    access(all) event RentCollectionInitialized(userAccountAddress: Address)
49    // Emitted when a Rent is minted
50    access(all) event RentMinted(id: UInt64, assetTokenID: UInt64, kID: String, assetURL: String, expiryTimestamp: UFix64)
51    // Emitted when a Rent is destroyed
52    access(all) event RentDestroyed(id: UInt64)
53    // Event that is emitted when a token is withdrawn, indicating the owner of the collection that it was withdrawn from.
54    access(all) event Withdraw(id: UInt64, from: Address?)
55    // Event that emitted when a token is deposited to a collection. It indicates the owner of the collection that it was deposited to.
56    access(all) event Deposit(id: UInt64, to: Address?)
57
58    /// Paths where Storage and capabilities are stored
59    access(all) let collectionStoragePath: StoragePath
60    access(all) let collectionPublicPath: PublicPath
61    access(all) let minterStoragePath: StoragePath
62    
63
64
65    access(all) entitlement RentUpdate
66    // RentData
67    //
68    // Struct for storing metadata for Rent
69    access(all) struct RentData {
70        access(all) let assetTokenID: UInt64
71        access(all) let kID: String
72        access(all) var assetName: String
73        access(all) var assetDescription: String
74        access(all) let assetURL: String
75        access(all) var assetThumbnailURL: String
76        access(all) var assetMetadata: {String: String}
77        access(all) var rentMetadata: {String: String}
78        access(all) let expiryTimestamp: UFix64
79
80        init(assetTokenID: UInt64, kID: String, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetMetadata: {String: String}, expiryTimestamp: UFix64) {
81            // pre {
82            //     expiryTimestamp > getCurrentBlock().timestamp:
83            //         "Expiry timestamp should be greater than current timestamp"
84            // }
85            self.assetTokenID = assetTokenID
86            self.kID = kID
87            self.assetName = assetName
88            self.assetDescription = assetDescription
89            self.assetURL = assetURL
90            self.assetThumbnailURL = assetThumbnailURL
91            self.assetMetadata = assetMetadata
92            self.assetMetadata = {}
93            self.rentMetadata = {}
94            self.expiryTimestamp = expiryTimestamp
95        }
96    }
97
98    //  NFT
99    //
100    // The main Rent NFT resource that signifies that an asset is rented by a user
101    access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
102        access(all) let id: UInt64
103        access(all) let data: RentData
104
105        access(all) view fun getViews(): [Type] {
106            return [
107                Type<MetadataViews.Display>()
108            ]
109        }
110
111        access(all) fun resolveView(_ view: Type): AnyStruct? {
112            switch view {
113                case Type<MetadataViews.Display>():
114                    return MetadataViews.Display(
115                        name: self.data.assetName.concat("(Rented)"),
116                        description: self.data.assetDescription,
117                        thumbnail: MetadataViews.HTTPFile(
118                            url: self.data.assetThumbnailURL
119                        )
120                    )
121            }
122
123            return nil
124        }
125
126        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
127            return <-TrmRentV2_2.createEmptyCollection(nftType: Type<@TrmRentV2_2.NFT>())
128        }
129
130        init(id: UInt64, assetTokenID: UInt64, kID: String, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetMetadata: {String: String}, expiryTimestamp: UFix64) {
131            self.id = id
132            self.data = RentData(assetTokenID: assetTokenID, kID: kID, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetMetadata: assetMetadata, expiryTimestamp: expiryTimestamp)
133
134            emit RentMinted(id: self.id, assetTokenID: assetTokenID, kID: kID, assetURL: assetURL, expiryTimestamp: expiryTimestamp)
135        }
136
137        // If the Rent NFT is destroyed, emit an event to indicate to outside ovbservers that it has been destroyed
138       
139    }
140
141    // CollectionPublic
142    //
143    // Public interface for Rent Collection
144    // This exposes functions for depositing NFTs
145    // and also for returning some info for a specific
146    // Rent NFT id and Asset NFT id
147    access(all) resource interface CollectionPublic {
148        access(contract) fun depositRent(token: @{NonFungibleToken.NFT})
149
150        access(all) fun getIDs(): [UInt64]
151        access(all) fun getAssetTokenIDs(): [UInt64]
152        access(all) fun getID(assetTokenID: UInt64): UInt64
153        access(all) fun getAssetTokenID(id: UInt64): UInt64
154        access(all) fun idExists(id: UInt64): Bool
155        access(all) fun assetTokenIDExists(assetTokenID: UInt64): Bool
156        access(all) fun borrowNFT(_ id: UInt64): &NFT
157        access(all) fun borrowNFTUsingAssetTokenID(assetTokenID: UInt64): &{NonFungibleToken.NFT}?
158        access(all) fun borrowRent(id: UInt64): &NFT
159        access(all) fun borrowRentUsingAssetTokenID(assetTokenID: UInt64): &NFT
160        access(all) fun getExpiryTimestamp(id: UInt64): UFix64
161        access(all) fun getExpiryTimestampUsingAssetTokenID(assetTokenID: UInt64): UFix64
162        access(all) fun isExpired(id: UInt64): Bool
163        access(all) fun isExpiredUsingAssetTokenID(assetTokenID: UInt64): Bool
164        access(all) fun isRentValid(id: UInt64): Bool
165        access(all) fun isRentValidUsingAssetTokenID(assetTokenID: UInt64): Bool
166    }
167
168    // Collection
169    //
170    // The resource that stores a user's Rent NFT collection.
171    // It includes a few functions to allow the owner to easily
172    // moveto deposit or withdraw the tokens.
173    access(all) resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, ViewResolver.ResolverCollection, CollectionPublic {
174        
175        // Dictionary to hold the Rent NFTs in the Collection
176        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
177        // Dictionary mapping Asset Token IDs to Rent Token IDs
178        access(all) var rentedNFTs: {UInt64: UInt64}
179
180        init() {
181            self.ownedNFTs <- {}
182            self.rentedNFTs = {}
183        }
184
185        // withdraw removes an NFT from the collection and moves it to the caller
186        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
187            pre {
188                false: "Withdrawing Rent directly from Rent contract is not allowed"
189            }
190            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Rent Token ID does not exist")
191            let rentToken <- token as! @NFT
192            self.rentedNFTs.remove(key: rentToken.data.assetTokenID)
193            emit Withdraw(id: withdrawID, from: self.owner!.address)
194            return <- rentToken
195        }
196
197        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
198            let supportedTypes: {Type: Bool} = {}
199            supportedTypes[Type<@TrmRentV2_2.NFT>()] = true
200            return supportedTypes
201        }
202
203        /// Returns whether or not the given type is accepted by the collection
204        /// A collection that can accept any type should just return true by default
205        access(all) view fun isSupportedNFTType(type: Type): Bool {
206            return type == Type<@TrmRentV2_2.NFT>()
207        }
208
209        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
210            return <-TrmRentV2_2.createEmptyCollection(nftType: Type<@TrmRentV2_2.NFT>())
211        }
212
213
214        // deposit takes an NFT as an argument and adds it to the Collection
215        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
216            pre {
217                false: "Depositing Rent directly to Rent contract is not allowed"
218            }
219
220            let rentToken <- token as! @NFT
221
222            if self.isRentValidUsingAssetTokenID(assetTokenID: rentToken.data.assetTokenID) {
223                destroy rentToken
224                panic("Valid Rent token for this asset already exists")
225            } else {
226                if let existingRentTokenID = self.rentedNFTs[rentToken.data.assetTokenID] {
227                    let existingRentToken <- self.withdrawRent(id: existingRentTokenID)
228                    destroy existingRentToken
229                }
230                let newRentTokenId = rentToken.id
231                self.rentedNFTs[rentToken.data.assetTokenID] = newRentTokenId
232                let oldToken <- self.ownedNFTs[newRentTokenId] <- rentToken
233                destroy oldToken
234                emit Deposit(id: newRentTokenId, to: self.owner!.address)
235            }
236        }
237
238        // getIDs returns an array of the IDs that are in the collection
239        access(all) view fun getIDs(): [UInt64] {
240            return self.ownedNFTs.keys
241        }
242
243        // borrowViewResolver is to conform with MetadataViews
244        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver} {
245            let refNFT = (&self.ownedNFTs[id] as auth(RentUpdate) &{NonFungibleToken.NFT}?)!
246            let refRentNFT = refNFT as! &NFT
247
248            return refRentNFT as &{ViewResolver.Resolver}
249        }
250
251        // getAssetTokenIDs returns an array of the asset token IDs that are in the collection
252        access(all) fun getAssetTokenIDs(): [UInt64] {
253            return self.rentedNFTs.keys
254        }
255
256        // Returns the rent token ID for an NFT from assetTokenID in the collection
257        access(all) fun getID(assetTokenID: UInt64): UInt64 {
258            return self.rentedNFTs[assetTokenID] ?? panic("Asset Token ID does not exist")
259        }
260
261        // Returns the asset token ID for an NFT in the collection
262        access(all) fun getAssetTokenID(id: UInt64): UInt64 {
263            pre {
264                self.ownedNFTs[id] != nil: "Rent Token ID does not exist"
265            }
266            let refNFT = (&self.ownedNFTs[id] as auth(RentUpdate) &{NonFungibleToken.NFT}?)!
267            let refRentNFT = refNFT as! &NFT
268            
269            return refRentNFT.data.assetTokenID
270        }
271
272        // Checks if id of NFT exists in collection
273        access(all) fun idExists(id: UInt64): Bool {
274            return self.ownedNFTs[id] != nil
275        }
276
277        // Checks if id of NFT exists in collection
278        access(all) fun assetTokenIDExists(assetTokenID: UInt64): Bool {
279            if (self.rentedNFTs[assetTokenID] == nil) {
280                return false
281            }
282            let rentTokenID = self.rentedNFTs[assetTokenID] ?? panic("Rent Token ID does not exist")
283            return self.ownedNFTs[rentTokenID] != nil
284        }
285
286        // Returns a borrowed reference to an NFT in the collection so that the caller can read data and call methods from it
287        access(all) view fun borrowNFT(_ id: UInt64): &NFT {
288            let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
289            return refNFT as! &NFT
290        }
291
292        // 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
293        access(all) fun borrowNFTUsingAssetTokenID(assetTokenID: UInt64): &{NonFungibleToken.NFT}? {
294            let rentTokenID = self.rentedNFTs[assetTokenID] ?? panic("Rent Token ID does not exist")
295            return &self.ownedNFTs[rentTokenID] as &{NonFungibleToken.NFT}?
296        }
297
298        // Returns a borrowed reference to the Rent NFT in the collection so that the caller can read data and call methods from it
299        access(all) fun borrowRent(id: UInt64): &NFT {
300            pre {
301                self.ownedNFTs[id] != nil: "Rent Token ID does not exist"
302            }
303            let refNFT = (&self.ownedNFTs[id] as auth(RentUpdate) &{NonFungibleToken.NFT}?)!
304            return refNFT as! &NFT
305        }
306
307        // 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
308        access(all) fun borrowRentUsingAssetTokenID(assetTokenID: UInt64): &NFT {
309            pre {
310                self.rentedNFTs[assetTokenID] != nil: "Asset Token ID does not exist"
311                self.ownedNFTs[self.rentedNFTs[assetTokenID]!] != nil: "Rent Token ID does not exist"
312            }
313            let rentTokenID = self.rentedNFTs[assetTokenID]!
314            let refNFT = (&self.ownedNFTs[rentTokenID] as auth(RentUpdate) &{NonFungibleToken.NFT}?)!
315            return refNFT as! &NFT
316        }
317
318        // Returns the expiry for an NFT in the collection
319        access(all) fun getExpiryTimestamp(id: UInt64): UFix64 {
320            pre {
321                self.ownedNFTs[id] != nil: "Rent Token ID does not exist"
322            }
323            let refNFT = (&self.ownedNFTs[id] as auth(RentUpdate) &{NonFungibleToken.NFT}?)!
324            let refRentNFT = refNFT as! &NFT
325            return refRentNFT.data.expiryTimestamp
326        }
327
328        // Returns the expiry for an NFT in the collection using asset token id
329        access(all) fun getExpiryTimestampUsingAssetTokenID(assetTokenID: UInt64): UFix64 {
330            pre {
331                self.rentedNFTs[assetTokenID] != nil: "Asset Token ID does not exist"
332                self.ownedNFTs[self.rentedNFTs[assetTokenID]!] != nil: "Rent Token ID does not exist"
333            }
334            let rentTokenID = self.rentedNFTs[assetTokenID]!
335
336            let refNFT = (&self.ownedNFTs[rentTokenID] as auth(RentUpdate) &{NonFungibleToken.NFT}?)!
337            let refRentNFT = refNFT as! &NFT
338            return refRentNFT.data.expiryTimestamp
339        }
340
341        // Returns if token is expired
342        access(all) fun isExpired(id: UInt64): Bool {
343            pre {
344                self.ownedNFTs[id] != nil: "Rent Token ID does not exist"
345            }
346            let refNFT = (&self.ownedNFTs[id] as auth(RentUpdate) &{NonFungibleToken.NFT}?)!
347            let refRentNFT = refNFT as! &NFT
348            return refRentNFT.data.expiryTimestamp < getCurrentBlock().timestamp
349        }
350
351        // Returns if token is expired using asset token id
352        access(all) fun isExpiredUsingAssetTokenID(assetTokenID: UInt64): Bool {
353            pre {
354                self.rentedNFTs[assetTokenID] != nil: "Asset Token ID does not exist"
355                self.ownedNFTs[self.rentedNFTs[assetTokenID]!] != nil: "Rent Token ID does not exist"
356            }
357            let rentTokenID = self.rentedNFTs[assetTokenID]!
358
359            let refNFT = (&self.ownedNFTs[rentTokenID] as auth(RentUpdate) &{NonFungibleToken.NFT}?)!
360            let refRentNFT = refNFT as! &NFT
361            return refRentNFT.data.expiryTimestamp < getCurrentBlock().timestamp
362        }
363
364        // Returns if rent is valid for rent id
365        access(all) fun isRentValid(id: UInt64): Bool {
366            if self.ownedNFTs[id] != nil {
367
368                let refNFT = (&self.ownedNFTs[id] as auth(RentUpdate) &{NonFungibleToken.NFT}?)!
369                let refRentNFT = refNFT as! &NFT
370                return refRentNFT.data.expiryTimestamp > getCurrentBlock().timestamp
371
372            } else {
373                return false
374            }
375        }
376
377        // Returns if rent is valid for asset token id
378        access(all) fun isRentValidUsingAssetTokenID(assetTokenID: UInt64): Bool {
379            if let rentTokenID = self.rentedNFTs[assetTokenID] {
380
381                if self.ownedNFTs[rentTokenID] != nil {
382
383                    let refNFT = (&self.ownedNFTs[rentTokenID] as auth(RentUpdate) &{NonFungibleToken.NFT}?)!
384                    let refRentNFT = refNFT as! &NFT
385                    return refRentNFT.data.expiryTimestamp > getCurrentBlock().timestamp
386                    
387                } else {
388                    return false
389                }
390            }
391            return false
392        }
393
394        // Destroys specified token in the collection
395        access(contract) fun destroyToken(id: UInt64) {
396            pre {
397                self.ownedNFTs[id] != nil: "Rent Token ID does not exist"
398            }
399            self.rentedNFTs.remove(key: self.getAssetTokenID(id: id))
400            let oldToken <- self.ownedNFTs.remove(key: id) 
401            destroy oldToken
402        }
403
404        // withdraw removes an NFT from the collection and moves it to the caller
405        access(contract) fun withdrawRent(id: UInt64): @{NonFungibleToken.NFT} {
406            let token <- self.ownedNFTs.remove(key: id) ?? panic("Rent Token ID does not exist")
407            let rentToken <- token as! @NFT
408            self.rentedNFTs.remove(key: rentToken.data.assetTokenID)
409            emit Withdraw(id: id, from: self.owner!.address)
410            return <- rentToken
411        }
412
413        // deposit takes an NFT as an argument and adds it to the Collection
414        access(contract) fun depositRent(token: @{NonFungibleToken.NFT}) {
415            let rentToken <- token as! @NFT
416
417            if self.isRentValidUsingAssetTokenID(assetTokenID: rentToken.data.assetTokenID) {
418                destroy rentToken
419                panic("Valid Rent token for this asset already exists")
420            } else {
421                if let existingRentTokenID = self.rentedNFTs[rentToken.data.assetTokenID] {
422                    let existingRentToken <- self.withdrawRent(id: existingRentTokenID)
423                    destroy existingRentToken
424                }
425                let newRentTokenId = rentToken.id
426                self.rentedNFTs[rentToken.data.assetTokenID] = newRentTokenId
427                let oldToken <- self.ownedNFTs[newRentTokenId] <- rentToken
428                destroy oldToken
429                emit Deposit(id: newRentTokenId, to: self.owner!.address)
430            }
431        }
432
433        // If a transaction destroys the Collection object, all the NFTs contained within are also destroyed!
434       
435    }
436
437    // createEmptyCollection creates an empty Collection and returns it to the caller so that they can own NFTs
438    access(all) fun createEmptyCollection(nftType: Type): @Collection {
439        return <- create Collection() 
440    }
441
442    // emitCreateEmptyRentCollectionEvent emits events for asset collection initialization
443    access(all) fun emitCreateEmptyRentCollectionEvent(userAccountAddress: Address) {
444        emit RentCollectionInitialized(userAccountAddress: userAccountAddress)
445    }
446
447    
448    // Minter
449    //
450    // Minter is a special resource that is used for minting Rent Tokens
451    access(all) resource Minter {
452
453        // mintNFT mints the rent NFT and stores it in the collection of recipient
454        access(all) fun mintNFT(assetTokenID: UInt64, kID: String, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetMetadata: {String: String}, expiryTimestamp: UFix64, recipient: &Collection): UInt64 {
455            // pre {
456            //     expiryTimestamp > getCurrentBlock().timestamp:
457            //         "Expiry timestamp should be greater than current timestamp"
458            // }
459            
460            let id = TrmRentV2_2.totalSupply
461            TrmRentV2_2.totalSupply = id + 1
462
463            recipient.depositRent(token: <- create NFT(id: id, assetTokenID: assetTokenID, kID: kID, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetMetadata: assetMetadata, expiryTimestamp: expiryTimestamp))
464            
465            return id
466        }
467    }
468
469    init() {
470        self.totalSupply = 0
471
472        // Settings paths
473        self.collectionStoragePath = /storage/TrmRentV2_2Collection
474        self.collectionPublicPath = /public/TrmRentV2_2Collection
475        self.minterStoragePath = /storage/TrmRentV2_2Minter
476        
477
478        // First, check to see if a minter resource already exists
479        if self.account.storage.type(at: self.minterStoragePath) == nil {
480
481            // Put the minter in storage with access only to admin
482            self.account.storage.save(<-create Minter(), to: self.minterStoragePath)
483        }
484        //self.account.link<&TrmRentV2_2.Minter>(self.minterPrivatePath, target: TrmRentV2_2.minterStoragePath)
485        let pub_cap = self.account.capabilities.storage.issue<&TrmRentV2_2.Minter>(self.minterStoragePath)
486        //self.account.storage.save(pub_cap, to:self.minterStoragePath)
487        
488
489        emit ContractInitialized()
490    }
491    
492    /// Function that returns all the Metadata Views implemented by a Non Fungible Token
493    ///
494    /// @return An array of Types defining the implemented views. This value will be used by
495    ///         developers to know which parameter to pass to the resolveView() method.
496    ///
497    access(all) view fun getContractViews(resourceType: Type?): [Type] {
498        return [
499            Type<MetadataViews.NFTCollectionDisplay>()
500        ]
501    }
502    access(all) fun resolveContractView(resourceType: Type?,viewType: Type): AnyStruct? {
503        // Check the type of the view requested and return the corresponding view
504        switch viewType {
505                case Type<MetadataViews.NFTCollectionDisplay>():
506                    return MetadataViews.Display(
507                        name: "Test",
508                        description: "Test ",
509                        thumbnail: MetadataViews.HTTPFile(
510                            url: ""
511                        )
512                    )
513            }
514
515        // Implement additional views if necessary
516        return nil
517    }
518   
519}
520