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