Smart Contract
TrmRentV2_1
A.61fc4b873e58733b.TrmRentV2_1
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}