Smart Contract
TrmAssetV2_2
A.61fc4b873e58733b.TrmAssetV2_2
1/**
2
3 TrmAssetV2_2.cdc
4
5 Description: Contract definitions for initializing Asset Collections, Asset NFT Resource and Asset Minter
6
7 Asset contract is used for defining the Asset NFT, Asset Collection,
8 Asset Collection Public Interface and Asset Minter
9
10 ## `NFT` resource
11
12 The core resource type that represents an Asset NFT in the smart contract.
13
14 ## `Collection` Resource
15
16 The resource that stores a user's Asset 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 Asset NFTs to the Asset Collectio.
23 It also exposes few functions to fetch data about Asset
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 Asset NFTs
32
33*/
34
35import NonFungibleToken from 0x1d7e57aa55817448
36import MetadataViews from 0x1d7e57aa55817448
37import ViewResolver from 0x1d7e57aa55817448
38
39access(all) contract TrmAssetV2_2: NonFungibleToken {
40 // -----------------------------------------------------------------------
41 // Asset contract Event definitions
42 // -----------------------------------------------------------------------
43
44 // The total number of tokens of this type in existence
45 access(all) var totalSupply: UInt64
46
47 // Event that emitted when the NFT contract is initialized
48 access(all) event ContractInitialized()
49
50 // Event that emitted when the asset collection is initialized
51 access(all) event AssetCollectionInitialized(userAccountAddress: Address)
52
53 // Emitted when an Asset is minted
54 access(all) event AssetMinted(id: UInt64, kID: String, serialNumber: UInt64, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetType: String, assetMetadata: {String: String}, owner: Address?)
55
56 // Emitted when a batch of Assets are minted successfully
57 access(all) event AssetBatchMinted(startId: UInt64, endId: UInt64, kID: String, totalCount: UInt64, startSerialNumber: UInt64, endSerialNumber: UInt64, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetType: String, assetMetadata: {String: String}, owner: Address?)
58
59 // Emitted when an Asset is updated
60 access(all) event AssetUpdated(id: UInt64, assetName: String?, assetDescription: String?, assetThumbnailURL: String?, assetType: String?, assetMetadata: {String: String}?, owner: Address?)
61
62 // Emitted when a batch of Assets are updated successfully
63 access(all) event AssetBatchUpdated(ids: [UInt64], kID: String, assetName: String?, assetDescription: String?, assetThumbnailURL: String?, assetType: String?, assetMetadata: {String: String}?, owner: Address?)
64
65 // Emitted when an Asset is destroyed
66 access(all) event AssetDestroyed(id: UInt64)
67
68 // Emitted when an Asset metadata entry is updated
69 access(all) event AssetMetadataEntryAdded(id: UInt64, key: String, value: String)
70
71 // Emitted when an Asset metadata entry is updated
72 access(all) event AssetMetadataEntryBatchAdded(ids: [UInt64], kID: String, key: String, value: String)
73
74 // Emitted when an Asset metadata entry is updated
75 access(all) event AssetMetadataEntryUpdated(id: UInt64, key: String, value: String)
76
77 // Emitted when an Asset metadata entry is updated
78 access(all) event AssetMetadataEntryBatchUpdated(ids: [UInt64], kID: String, key: String, value: String)
79
80 // Emitted when an Asset metadata entry is updated
81 access(all) event AssetMetadataEntryRemoved(id: UInt64, key: String)
82
83 // Emitted when an Asset metadata entry is updated
84 access(all) event AssetMetadataEntryBatchRemoved(ids: [UInt64], kID: String, key: String)
85
86 // Event that is emitted when a token is withdrawn, indicating the owner of the collection that it was withdrawn from. If the collection is not in an account's storage, `from` will be `nil`.
87 access(all) event Withdraw(id: UInt64, from: Address?)
88
89 // Event that emitted when a token is deposited to a collection. It indicates the owner of the collection that it was deposited to.
90 access(all) event Deposit(id: UInt64, to: Address?)
91
92 // Event that is emitted when an invitee is added to the Invitee List
93 access(all) event Invite(id: UInt64, invitee: Address)
94
95 // Event that is emitted when an invitee is removed to the Invitee List
96 access(all) event Disinvite(id: UInt64, invitee: Address)
97
98 /// Paths where Storage and capabilities are stored
99 access(all) let collectionStoragePath: StoragePath
100 access(all) let collectionPublicPath: PublicPath
101 access(all) let collectionPrivatePath: PrivatePath
102 access(all) let minterStoragePath: StoragePath
103 access(all) let adminStoragePath: StoragePath
104
105 access(all) entitlement Update
106
107
108
109 access(all) enum AssetType: UInt8 {
110 access(all) case private
111 access(all) case public
112 }
113
114 access(all) fun assetTypeToString(_ assetType: AssetType): String {
115 switch assetType {
116 case AssetType.private:
117 return "private"
118 case AssetType.public:
119 return "public"
120 default:
121 return ""
122 }
123 }
124
125 access(all) fun stringToAssetType(_ assetTypeStr: String): AssetType {
126 switch assetTypeStr {
127 case "private":
128 return AssetType.private
129 case "public":
130 return AssetType.public
131 default:
132 return panic("Asset Type must be \"private\" or \"public\"")
133 }
134 }
135
136 // AssetData
137 //
138 // Struct for storing metadata for Asset
139 access(all) struct AssetData {
140 access(all) let kID: String
141 access(all) let serialNumber: UInt64
142 access(all) var assetName: String
143 access(all) var assetDescription: String
144 access(all) let assetURL: String
145 access(all) var assetThumbnailURL: String
146 access(all) var assetType: AssetType
147 access(all) var assetMetadata: {String: String}
148 access(all) var invitees: [Address]
149
150 access(contract) fun setAssetName(assetName: String) {
151 self.assetName = assetName
152 }
153
154 access(contract) fun setAssetDescription(assetDescription: String) {
155 self.assetDescription = assetDescription
156 }
157
158 access(contract) fun setAssetThumbnailURL(assetThumbnailURL: String) {
159 self.assetThumbnailURL = assetThumbnailURL
160 }
161
162 access(contract) fun setAssetType(assetType: String) {
163 self.assetType = TrmAssetV2_2.stringToAssetType(assetType)
164 }
165
166 access(contract) fun setAssetMetadata(assetMetadata: {String: String}) {
167 self.assetMetadata = assetMetadata
168 }
169
170 access(contract) fun addAssetMetadataEntry(key: String, value: String) {
171 self.assetMetadata[key] = value
172 }
173
174 access(contract) fun setAssetMetadataEntry(key: String, value: String) {
175 pre {
176 self.assetMetadata.containsKey(key):
177 "No entry matching this key in metadata!"
178 }
179 self.assetMetadata[key] = value
180 }
181
182 access(contract) fun removeAssetMetadataEntry(key: String) {
183 pre {
184 self.assetMetadata.containsKey(key):
185 "No entry matching this key in metadata!"
186 }
187 self.assetMetadata.remove(key: key)
188 }
189
190 access(contract) fun invite(invitee: Address) {
191 if !self.invitees.contains(invitee) {
192 self.invitees.append(invitee)
193 }
194 }
195
196 access(contract) fun disinvite(invitee: Address) {
197 if let inviteeIndex = self.invitees.firstIndex(of: invitee) {
198 self.invitees.remove(at: inviteeIndex)
199 }
200 }
201
202 access(contract) fun inviteeExists(invitee: Address): Bool {
203 return self.invitees.contains(invitee)
204 }
205
206 access(contract) fun removeInvitees() {
207 self.invitees = []
208 }
209
210 init(kID: String, serialNumber: UInt64, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetType: String, assetMetadata: {String: String}) {
211 pre {
212 serialNumber >= 0: "Serial Number cannot be less than 0"
213
214 kID.length > 0: "KID is invalid"
215 }
216
217 self.kID = kID
218 self.serialNumber = serialNumber
219 self.assetName = assetName
220 self.assetDescription = assetDescription
221 self.assetURL = assetURL
222 self.assetThumbnailURL = assetThumbnailURL
223 self.assetType = TrmAssetV2_2.stringToAssetType(assetType)
224 self.assetMetadata = assetMetadata
225 self.invitees = []
226 }
227 }
228
229 // NFT
230 //
231 // The main Asset NFT resource that can be bought and sold by users
232 access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver{
233 access(all) let id: UInt64
234 access(all) let data: AssetData
235
236 access(all) view fun getViews(): [Type] {
237 return [
238 Type<MetadataViews.Display>()
239 ]
240 }
241
242
243 access(all) fun resolveView(_ view: Type): AnyStruct? {
244 switch view {
245 case Type<MetadataViews.Display>():
246 return MetadataViews.Display(
247 name: self.data.assetName,
248 description: self.data.assetDescription,
249 thumbnail: MetadataViews.HTTPFile(
250 url: self.data.assetThumbnailURL
251 )
252 )
253 }
254
255 return nil
256 }
257
258
259
260 access(contract) fun updateAsset(assetName: String?, assetDescription: String?, assetThumbnailURL: String?, assetType: String?, assetMetadata: {String: String}?) {
261 if (assetName != nil) {
262 self.data.setAssetName(assetName: assetName!)
263 }
264 if (assetDescription != nil) {
265 self.data.setAssetDescription(assetDescription: assetDescription!)
266 }
267 if (assetThumbnailURL != nil) {
268 self.data.setAssetThumbnailURL(assetThumbnailURL: assetThumbnailURL!)
269 }
270 if (assetType != nil) {
271 self.data.setAssetType(assetType: assetType!)
272 }
273 if (assetMetadata != nil) {
274 self.data.setAssetMetadata(assetMetadata: assetMetadata!)
275 }
276 }
277
278 access(contract) fun addAssetMetadataEntry(key: String, value: String) {
279 self.data.addAssetMetadataEntry(key: key, value: value)
280
281 emit AssetMetadataEntryAdded(id: self.id, key: key, value: value)
282 }
283
284 access(contract) fun setAssetMetadataEntry(key: String, value: String) {
285 pre {
286 self.data.assetMetadata.containsKey(key):
287 "No entry matching this key in metadata!"
288 }
289 self.data.setAssetMetadataEntry(key: key, value: value)
290
291 emit AssetMetadataEntryUpdated(id: self.id, key: key, value: value)
292 }
293
294 access(contract) fun removeAssetMetadataEntry(key: String) {
295 pre {
296 self.data.assetMetadata.containsKey(key):
297 "No entry matching this key in metadata!"
298 }
299 self.data.removeAssetMetadataEntry(key: key)
300
301 emit AssetMetadataEntryRemoved(id: self.id, key: key)
302 }
303
304 access(contract) fun invite(invitee: Address) {
305 if !self.data.invitees.contains(invitee) {
306 self.data.invite(invitee: invitee)
307 emit Invite(id: self.id, invitee: invitee)
308 }
309 }
310
311 access(contract) fun disinvite(invitee: Address) {
312 if let inviteeIndex = self.data.invitees.firstIndex(of: invitee) {
313 self.data.disinvite(invitee: invitee)
314 emit Disinvite(id: self.id, invitee: invitee)
315 }
316 }
317
318 access(contract) fun inviteeExists(invitee: Address): Bool {
319 return self.data.inviteeExists(invitee: invitee)
320 }
321
322 access(contract) fun removeInvitees() {
323 self.data.removeInvitees()
324 }
325
326 init(id: UInt64, kID: String, serialNumber: UInt64, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetType: String, assetMetadata: {String: String}) {
327 self.id = id
328 self.data = AssetData(kID: kID, serialNumber: serialNumber, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata)
329 }
330
331 // If the Asset NFT is destroyed, emit an event to indicate to outside observers that it has been destroyed
332
333
334
335
336 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
337 return <-TrmAssetV2_2.createEmptyCollection(nftType: Type<@TrmAssetV2_2.NFT>())
338 }
339}
340
341 // CollectionPublic
342 //
343 // Public interface for Asset Collection
344 // This exposes functions for depositing NFTs
345 // and also for returning some info for a specific
346 // Asset NFT id
347 access(all) resource interface CollectionPublic {
348 access(contract) fun withdrawAsset(id: UInt64): @{NonFungibleToken.NFT}
349 access(contract) fun depositAsset(token: @{NonFungibleToken.NFT})
350 access(contract) fun updateAsset(id: UInt64, assetName: String?, assetDescription: String?, assetThumbnailURL: String?, assetType: String?, assetMetadata: {String: String}?)
351 access(contract) fun batchUpdateAsset(ids: [UInt64], kID: String, assetName: String?, assetDescription: String?, assetThumbnailURL: String?, assetType: String?, assetMetadata: {String: String}?)
352 access(contract) fun addAssetMetadataEntry(id: UInt64, key: String, value: String)
353 access(contract) fun batchAddAssetMetadataEntry(ids: [UInt64], kID: String, key: String, value: String)
354 access(contract) fun setAssetMetadataEntry(id: UInt64, key: String, value: String)
355 access(contract) fun batchSetAssetMetadataEntry(ids: [UInt64], kID: String, key: String, value: String)
356 access(contract) fun removeAssetMetadataEntry(id: UInt64, key: String)
357 access(contract) fun batchRemoveAssetMetadataEntry(ids: [UInt64], kID: String, key: String)
358 access(contract) fun invite(id: UInt64, invitee: Address)
359 access(contract) fun disinvite(id: UInt64, invitee: Address)
360 access(contract) fun destroyToken(id: UInt64)
361
362 access(all) fun getIDs(): [UInt64]
363 access(all) fun borrowNFT(_ id: UInt64): &NFT
364 access(all) fun borrowAsset(id: UInt64): &NFT
365 access(all) view fun idExists(id: UInt64): Bool
366 access(all) fun getKID(id: UInt64): String
367 access(all) fun getSerialNumber(id: UInt64): UInt64
368 access(all) fun getAssetName(id: UInt64): String
369 access(all) fun getAssetDescription(id: UInt64): String
370 access(all) fun getAssetURL(id: UInt64): String
371 access(all) fun getAssetThumbnailURL(id: UInt64): String
372 access(all) fun getAssetType(id: UInt64): String
373 access(all) fun getAssetMetadata(id: UInt64): {String: String}
374 access(all) fun getInvitees(id: UInt64): [Address]
375 access(all) fun inviteeExists(id: UInt64, invitee: Address): Bool
376 }
377
378 // Collection
379 //
380 // The resource that stores a user's Asset NFT collection.
381 // It includes a few functions to allow the owner to easily
382 // move tokens in and out of the collection.
383 access(all) resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, CollectionPublic, ViewResolver.ResolverCollection {
384
385 // Dictionary to hold the NFTs in the Collection
386 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
387
388 init() {
389 self.ownedNFTs <- {}
390 }
391
392 // withdraw removes an NFT from the collection and moves it to the caller
393 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
394 pre {
395 false: "Withdrawing Asset directly from Asset contract is not allowed"
396 }
397 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Error withdrawing Asset NFT")
398 emit Withdraw(id: withdrawID, from: self.owner!.address)
399 return <-token
400 }
401
402 // deposit takes an NFT as an argument and adds it to the Collection
403 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
404 pre {
405 false: "Depositing Asset directly to Asset contract is not allowed"
406 }
407 let assetToken <- token as! @NFT
408 assetToken.removeInvitees()
409
410 emit Deposit(id: assetToken.id, to: self.owner!.address)
411
412 let oldToken <- self.ownedNFTs[assetToken.id] <- assetToken
413 destroy oldToken
414 }
415 access(all) view fun getIDs(): [UInt64] {
416 return self.ownedNFTs.keys
417 }
418
419 /// Gets the amount of NFTs stored in the collection
420 access(all) view fun getLength(): Int {
421 return self.ownedNFTs.length
422 }
423
424 // borrowViewResolver is to conform with MetadataViews
425
426
427 // Returns a borrowed reference to an NFT in the collection so that the caller can read data and call methods from it
428 access(all) view fun borrowNFT(_ id: UInt64): &NFT {
429 pre {
430 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
431 }
432 let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
433 return refNFT as! &NFT
434 }
435
436 // Returns a borrowed reference to the Asset in the collection so that the caller can read data and call methods from it
437 access(all) fun borrowAsset(id: UInt64): &NFT {
438 pre {
439 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
440 }
441 let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
442 return refNFT as! &NFT
443 }
444
445 // Checks if id of NFT exists in collection
446 access(all) view fun idExists(id: UInt64): Bool {
447 return self.ownedNFTs[id] != nil
448 }
449
450 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
451 return <-TrmAssetV2_2.createEmptyCollection(nftType: Type<@TrmAssetV2_2.NFT>())
452 }
453
454 // Returns the asset ID for an NFT in the collection
455 access(all) fun getKID(id: UInt64): String {
456 pre {
457 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
458 }
459 let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
460 let refAssetNFT = refNFT as! &NFT
461 return refAssetNFT.data.kID
462 }
463
464 // Returns the serial number for an NFT in the collection
465 access(all) fun getSerialNumber(id: UInt64): UInt64 {
466 pre {
467 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
468 }
469 let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
470 let refAssetNFT = refNFT as! &NFT
471 return refAssetNFT.data.serialNumber
472 }
473
474 // Returns the asset name for an NFT in the collection
475 access(all) fun getAssetName(id: UInt64): String {
476 pre {
477 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
478 }
479 let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
480 let refAssetNFT = refNFT as! &NFT
481 return refAssetNFT.data.assetName
482 }
483
484 // Returns the asset description for an NFT in the collection
485 access(all) fun getAssetDescription(id: UInt64): String {
486 pre {
487 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
488 }
489 let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
490 let refAssetNFT = refNFT as! &NFT
491 return refAssetNFT.data.assetDescription
492 }
493
494 // Returns the asset URL for an NFT in the collection
495 access(all) fun getAssetURL(id: UInt64): String {
496 pre {
497 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
498 }
499 let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
500 let refAssetNFT = refNFT as! &NFT
501 return refAssetNFT.data.assetURL
502 }
503
504 // Returns the asset thumbnail URL for an NFT in the collection
505 access(all) fun getAssetThumbnailURL(id: UInt64): String {
506 pre {
507 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
508 }
509 let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
510 let refAssetNFT = refNFT as! &NFT
511 return refAssetNFT.data.assetThumbnailURL
512 }
513
514 // Returns the asset type for an NFT in the collection
515 access(all) fun getAssetType(id: UInt64): String {
516 pre {
517 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
518 }
519 let refNFT: &{NonFungibleToken.NFT} = (&self.ownedNFTs[id])!
520 let refAssetNFT = refNFT as! &NFT
521 let refAssetNFTData = refAssetNFT.data
522 return TrmAssetV2_2.assetTypeToString(refAssetNFTData.assetType)
523 }
524
525 // Returns the asset metadata for an NFT in the collection
526 access(all) fun getAssetMetadata(id: UInt64): {String: String} {
527 pre {
528 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
529 }
530 let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
531 let refAssetNFT = refNFT as! &NFT
532 let refAssetNFTData = refAssetNFT.data as! AssetData
533 return refAssetNFTData.assetMetadata
534 }
535
536 // Returns the invitees list for an NFT in the collection
537 access(all) fun getInvitees(id: UInt64): [Address] {
538 pre {
539 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
540 }
541 let refNFT = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
542 let refAssetNFT = refNFT as! &NFT
543 let refAssetNFTData = refAssetNFT.data as! AssetData
544 return refAssetNFTData.invitees
545 }
546
547 // withdraw removes an NFT from the collection and moves it to the caller
548 access(contract) fun withdrawAsset(id: UInt64): @{NonFungibleToken.NFT} {
549 let token <- self.ownedNFTs.remove(key: id) ?? panic("Error withdrawing Asset NFT")
550 emit Withdraw(id: id, from: self.owner!.address)
551 return <-token
552 }
553
554 // deposit takes an NFT as an argument and adds it to the Collection
555 access(contract) fun depositAsset(token: @{NonFungibleToken.NFT}) {
556 let assetToken <- token as! @NFT
557 assetToken.removeInvitees()
558
559 // This is removed as this was creating too many events in case of batch mint
560 // emit Deposit(id: assetToken.id, to: self.owner!.address)
561
562 let oldToken <- self.ownedNFTs[assetToken.id] <- assetToken
563 destroy oldToken
564 }
565
566 // Sets the asset type
567 access(contract) fun updateAsset(id: UInt64, assetName: String?, assetDescription: String?, assetThumbnailURL: String?, assetType: String?, assetMetadata: {String: String}?) {
568 pre {
569 assetType == "private" || assetType == "public" || assetType == nil:
570 "Asset Type must be private or public or null"
571
572 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
573 }
574
575 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
576 let refAssetNFT = refNFT as! &NFT
577 refAssetNFT.updateAsset(assetName: assetName, assetDescription: assetDescription, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata)
578
579 emit AssetUpdated(id: id, assetName: assetName, assetDescription: assetDescription, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata, owner: self.owner?.address)
580 }
581
582 // Sets the asset type of multiple tokens
583 access(contract) fun batchUpdateAsset(ids: [UInt64], kID: String, assetName: String?, assetDescription: String?, assetThumbnailURL: String?, assetType: String?, assetMetadata: {String: String}?) {
584 pre {
585 assetType == "private" || assetType == "public" || assetType == nil:
586 "Asset Type must be private or public or null"
587
588 ids.length > 0: "Total length of ids cannot be less than 1"
589 }
590
591 for id in ids {
592 if self.ownedNFTs[id] != nil {
593 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
594 let refAssetNFT = refNFT as! &NFT
595 if (refAssetNFT.data.kID != kID) {
596 panic("Asset Token ID and KID do not match")
597 }
598 refAssetNFT.updateAsset(assetName: assetName, assetDescription: assetDescription, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata)
599 } else {
600 panic("Asset Token ID ".concat(id.toString()).concat(" not owned"))
601 }
602 }
603
604 emit AssetBatchUpdated(ids: ids, kID: kID, assetName: assetName, assetDescription: assetDescription, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata, owner: self.owner?.address)
605 }
606
607 // Adds an entry to metadata in asset
608 access(contract) fun addAssetMetadataEntry(id: UInt64, key: String, value: String) {
609 pre {
610 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
611 }
612
613 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
614 let refAssetNFT = refNFT as! &NFT
615 refAssetNFT.addAssetMetadataEntry(key: key, value: value)
616 }
617
618 // Adds an entry to metadata in asset for multiple tokens
619 access(contract) fun batchAddAssetMetadataEntry(ids: [UInt64], kID: String, key: String, value: String) {
620 pre {
621 ids.length > 0: "Total length of ids cannot be less than 1"
622 }
623
624 for id in ids {
625 if self.ownedNFTs[id] != nil {
626 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
627 let refAssetNFT = refNFT as! &NFT
628 if (refAssetNFT.data.kID != kID) {
629 panic("Asset Token ID and KID do not match")
630 }
631 refAssetNFT.addAssetMetadataEntry(key: key, value: value)
632 } else {
633 panic("Asset Token ID ".concat(id.toString()).concat(" not owned"))
634 }
635 }
636
637 emit AssetMetadataEntryBatchAdded(ids: ids, kID: kID, key: key, value: value)
638 }
639
640 // Sets an entry to metadata in asset
641 access(contract) fun setAssetMetadataEntry(id: UInt64, key: String, value: String) {
642 pre {
643 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
644 }
645
646 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
647 let refAssetNFT = refNFT as! &NFT
648 refAssetNFT.setAssetMetadataEntry(key: key, value: value)
649 }
650
651 // Sets an entry to metadata in asset for multiple tokens
652 access(contract) fun batchSetAssetMetadataEntry(ids: [UInt64], kID: String, key: String, value: String) {
653 pre {
654 ids.length > 0: "Total length of ids cannot be less than 1"
655 }
656
657 for id in ids {
658 if self.ownedNFTs[id] != nil {
659 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
660 let refAssetNFT = refNFT as! &NFT
661 if (refAssetNFT.data.kID != kID) {
662 panic("Asset Token ID and KID do not match")
663 }
664 refAssetNFT.setAssetMetadataEntry(key: key, value: value)
665 } else {
666 panic("Asset Token ID ".concat(id.toString()).concat(" not owned"))
667 }
668 }
669
670 emit AssetMetadataEntryBatchUpdated(ids: ids, kID: kID, key: key, value: value)
671 }
672
673 // Removes an entry to metadata in asset
674 access(contract) fun removeAssetMetadataEntry(id: UInt64, key: String) {
675 pre {
676 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
677 }
678
679 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
680 let refAssetNFT = refNFT as! &NFT
681 refAssetNFT.removeAssetMetadataEntry(key: key)
682 }
683
684 // Removes an entry to metadata in asset for multiple tokens
685 access(contract) fun batchRemoveAssetMetadataEntry(ids: [UInt64], kID: String, key: String) {
686 pre {
687 ids.length > 0: "Total length of ids cannot be less than 1"
688 }
689
690 for id in ids {
691 if self.ownedNFTs[id] != nil {
692 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
693 let refAssetNFT = refNFT as! &NFT
694 if (refAssetNFT.data.kID != kID) {
695 panic("Asset Token ID and KID do not match")
696 }
697 refAssetNFT.removeAssetMetadataEntry(key: key)
698 } else {
699 panic("Asset Token ID ".concat(id.toString()).concat(" not owned"))
700 }
701 }
702
703 emit AssetMetadataEntryBatchRemoved(ids: ids, kID: kID, key: key)
704 }
705
706 // Add an invitee to invitee list
707 access(contract) fun invite(id: UInt64, invitee: Address) {
708 pre {
709 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
710 }
711
712 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
713 let refAssetNFT = refNFT as! &NFT
714 refAssetNFT.invite(invitee: invitee)
715 }
716
717 // Remove an invitee to invitee list. Only the owner of the asset is allowed to set this
718 access(contract) fun disinvite(id: UInt64, invitee: Address) {
719 pre {
720 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
721 }
722
723 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
724 let refAssetNFT = refNFT as! &NFT
725 refAssetNFT.disinvite(invitee: invitee)
726 }
727
728 access(all) fun inviteeExists(id: UInt64, invitee: Address): Bool {
729 pre {
730 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
731 }
732
733 let refNFT = (&self.ownedNFTs[id] as auth(Update) &{NonFungibleToken.NFT}?)!
734 let refAssetNFT = refNFT as! &NFT
735 return refAssetNFT.inviteeExists(invitee: invitee)
736 }
737
738 // Destroys specified token in the collection
739 access(contract) fun destroyToken(id: UInt64) {
740 pre {
741 self.ownedNFTs[id] != nil: "Asset Token ID does not exist"
742 }
743 let oldToken <- self.ownedNFTs.remove(key: id) ?? panic("Token not found.")
744 destroy oldToken
745 }
746
747 // If a transaction destroys the Collection object, all the NFTs contained within are also destroyed!
748
749
750
751
752 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
753 let supportedTypes: {Type: Bool} = {}
754 supportedTypes[Type<@TrmAssetV2_2.NFT>()] = true
755 return supportedTypes
756 }
757
758 /// Returns whether or not the given type is accepted by the collection
759 /// A collection that can accept any type should just return true by default
760 access(all) view fun isSupportedNFTType(type: Type): Bool {
761 return type == Type<@TrmAssetV2_2.NFT>()
762 }
763 /// createEmptyCollection creates an empty Collection of the same type
764 /// and returns it to the caller
765 /// @return A an empty collection of the same type
766
767 access(all) fun forEachID(_ f: fun (UInt64): Bool): Void {
768 self.ownedNFTs.forEachKey(f)
769 }
770}
771
772 // createEmptyCollection creates an empty Collection and returns it to the caller so that they can own NFTs
773 access(all) fun createEmptyCollection(nftType: Type): @Collection {
774 return <- create Collection()
775 }
776
777
778 // emitCreateEmptyAssetCollectionEvent emits events for asset collection initialization
779 access(all) fun emitCreateEmptyAssetCollectionEvent(userAccountAddress: Address) {
780 emit AssetCollectionInitialized(userAccountAddress: userAccountAddress)
781 }
782
783 // Minter
784 //
785 // Minter is a special resource that is used for minting Assets
786 access(all) resource Minter {
787
788 // mintNFT mints the asset NFT and stores it in the collection of recipient
789 access(all) fun mintNFT(kID: String, serialNumber: UInt64, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetType: String, assetMetadata: {String: String}, recipient: &TrmAssetV2_2.Collection): UInt64 {
790 pre {
791 assetType == "private" || assetType == "public":
792 "Asset Type must be private or public"
793
794 serialNumber >= 0: "Serial Number cannot be less than 0"
795
796 kID.length > 0: "KID is invalid"
797 }
798
799 let tokenID = TrmAssetV2_2.totalSupply
800
801 recipient.depositAsset(token: <- create NFT(id: tokenID, kID: kID, serialNumber: serialNumber, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata))
802
803 emit AssetMinted(id: tokenID, kID: kID, serialNumber: serialNumber, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata, owner: recipient.owner?.address)
804
805 TrmAssetV2_2.totalSupply = tokenID + 1
806
807 return TrmAssetV2_2.totalSupply
808 }
809
810 // batchMintNFTs mints the asset NFT in batch and stores it in the collection of recipient
811 access(all) fun batchMintNFTs(kID: String, totalCount: UInt64, startSerialNumber: UInt64, assetName: String, assetDescription: String, assetURL: String, assetThumbnailURL: String, assetType: String, assetMetadata: {String: String}, recipient: &TrmAssetV2_2.Collection): UInt64 {
812 pre {
813 assetType == "private" || assetType == "public":
814 "Asset Type must be private or public"
815
816 totalCount > 0: "Total Count cannot be less than 1"
817
818 startSerialNumber >= 0: "Start Serial Number cannot be less than 1"
819
820 assetURL.length > 0: "Asset URL is invalid"
821
822 kID.length > 0: "KID is invalid"
823 }
824
825 let startTokenID = TrmAssetV2_2.totalSupply
826 var tokenID = startTokenID
827 var counter: UInt64 = 0
828 var serialNumber = startSerialNumber
829
830 while counter < totalCount {
831
832 recipient.depositAsset(token: <- create NFT(id: tokenID, kID: kID, serialNumber: serialNumber, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata))
833
834 counter = counter + 1
835 tokenID = tokenID + 1
836 serialNumber = serialNumber + 1
837 }
838
839 let endTokenID = tokenID - 1
840 let endSerialNumber = serialNumber - 1
841
842 emit AssetBatchMinted(startId: startTokenID, endId: endTokenID, kID: kID, totalCount: totalCount, startSerialNumber: startSerialNumber, endSerialNumber: endSerialNumber, assetName: assetName, assetDescription: assetDescription, assetURL: assetURL, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata, owner: recipient.owner?.address)
843
844 TrmAssetV2_2.totalSupply = tokenID
845
846 return TrmAssetV2_2.totalSupply
847 }
848 }
849
850 /// Admin is a special authorization resource that
851 /// allows the admin to perform important functions
852 access(all) resource Admin {
853
854 access(all) fun withdrawAsset(
855 assetCollectionAddress: Address,
856 id: UInt64,
857 ): @{NonFungibleToken.NFT} {
858 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
859 TrmAssetV2_2.collectionPublicPath)
860 ?? panic("Could not borrow asset collection capability from provided asset collection address")
861
862 return <-assetCollectionCapability.withdrawAsset(id: id)
863 }
864
865 access(all) fun depositAsset(
866 assetCollectionAddress: Address,
867 token: @{NonFungibleToken.NFT}) {
868 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
869 TrmAssetV2_2.collectionPublicPath
870 )
871 ?? panic("Could not borrow asset collection capability from provided asset collection address")
872
873 assetCollectionCapability.depositAsset(token: <-token)
874 }
875
876 access(all) fun updateAsset(
877 assetCollectionAddress: Address,
878 id: UInt64,
879 assetName: String?,
880 assetDescription: String?,
881 assetThumbnailURL: String?,
882 assetType: String?,
883 assetMetadata: {String:String}?
884 ) {
885 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
886 TrmAssetV2_2.collectionPublicPath
887 )
888 ?? panic("Could not borrow asset collection capability from provided asset collection address")
889
890 assetCollectionCapability.updateAsset(id: id, assetName: assetName, assetDescription: assetDescription, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata)
891 }
892
893 access(all) fun batchUpdateAsset(
894 assetCollectionAddress: Address,
895 ids: [UInt64],
896 kID: String,
897 assetName: String?,
898 assetDescription: String?,
899 assetThumbnailURL: String?,
900 assetType: String?,
901 assetMetadata: {String: String}?
902 ) {
903 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
904 TrmAssetV2_2.collectionPublicPath
905 )
906 ?? panic("Could not borrow asset collection capability from provided asset collection address")
907
908 assetCollectionCapability.batchUpdateAsset(ids: ids, kID: kID, assetName: assetName, assetDescription: assetDescription, assetThumbnailURL: assetThumbnailURL, assetType: assetType, assetMetadata: assetMetadata)
909 }
910
911 access(all) fun addAssetMetadataEntry(
912 assetCollectionAddress: Address,
913 id: UInt64,
914 key: String,
915 value: String
916 ) {
917 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
918 TrmAssetV2_2.collectionPublicPath
919 )
920 ?? panic("Could not borrow asset collection capability from provided asset collection address")
921
922 assetCollectionCapability.addAssetMetadataEntry(id: id, key: key, value: value)
923 }
924
925 access(all) fun batchAddAssetMetadataEntry(
926 assetCollectionAddress: Address,
927 ids: [UInt64],
928 kID: String,
929 key: String,
930 value: String
931 ) {
932 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
933 TrmAssetV2_2.collectionPublicPath
934 )
935 ?? panic("Could not borrow asset collection capability from provided asset collection address")
936
937 assetCollectionCapability.batchAddAssetMetadataEntry(ids: ids, kID: kID, key: key, value: value)
938 }
939
940 access(all) fun setAssetMetadataEntry(
941 assetCollectionAddress: Address,
942 id: UInt64,
943 key: String,
944 value: String
945 ) {
946 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
947 TrmAssetV2_2.collectionPublicPath
948 )
949 ?? panic("Could not borrow asset collection capability from provided asset collection address")
950
951 assetCollectionCapability.setAssetMetadataEntry(id: id, key: key, value: value)
952 }
953
954 access(all) fun batchSetAssetMetadataEntry(
955 assetCollectionAddress: Address,
956 ids: [UInt64],
957 kID: String,
958 key: String,
959 value: String
960 ) {
961 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
962 TrmAssetV2_2.collectionPublicPath
963 )
964 ?? panic("Could not borrow asset collection capability from provided asset collection address")
965
966 assetCollectionCapability.batchSetAssetMetadataEntry(ids: ids, kID: kID, key: key, value: value)
967 }
968
969 access(all) fun removeAssetMetadataEntry(
970 assetCollectionAddress: Address,
971 id: UInt64,
972 key: String
973 ) {
974 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
975 TrmAssetV2_2.collectionPublicPath
976 )
977 ?? panic("Could not borrow asset collection capability from provided asset collection address")
978
979 assetCollectionCapability.removeAssetMetadataEntry(id: id, key: key)
980 }
981
982 access(all) fun batchRemoveAssetMetadataEntry(
983 assetCollectionAddress: Address,
984 ids: [UInt64],
985 kID: String,
986 key: String
987 ) {
988 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
989 TrmAssetV2_2.collectionPublicPath
990 )
991 ?? panic("Could not borrow asset collection capability from provided asset collection address")
992
993 assetCollectionCapability.batchRemoveAssetMetadataEntry(ids: ids, kID: kID, key: key)
994 }
995
996 access(all) fun invite(
997 assetCollectionAddress: Address,
998 id: UInt64,
999 invitee: Address
1000 ) {
1001 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
1002 TrmAssetV2_2.collectionPublicPath
1003 )
1004 ?? panic("Could not borrow asset collection capability from provided asset collection address")
1005
1006 assetCollectionCapability.invite(id: id, invitee: invitee)
1007 }
1008
1009 access(all) fun disinvite(
1010 assetCollectionAddress: Address,
1011 id: UInt64,
1012 invitee: Address
1013 ) {
1014 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
1015 TrmAssetV2_2.collectionPublicPath
1016 )
1017 ?? panic("Could not borrow asset collection capability from provided asset collection address")
1018
1019 assetCollectionCapability.disinvite(id: id, invitee: invitee)
1020 }
1021
1022 access(all) fun destroyToken(
1023 assetCollectionAddress: Address,
1024 id: UInt64
1025 ) {
1026 let assetCollectionCapability = getAccount(assetCollectionAddress).capabilities.borrow<&TrmAssetV2_2.Collection>(
1027 TrmAssetV2_2.collectionPublicPath
1028 )
1029 ?? panic("Could not borrow asset collection capability from provided asset collection address")
1030
1031 assetCollectionCapability.destroyToken(id: id)
1032 }
1033 }
1034
1035 init() {
1036 self.totalSupply = 0
1037
1038 // Settings paths
1039 self.collectionStoragePath = /storage/TrmAssetV2_2Collection
1040 self.collectionPublicPath = /public/TrmAssetV2_2Collection
1041 self.collectionPrivatePath = /private/TrmAssetV2_2Collection
1042 self.minterStoragePath = /storage/TrmAssetV2_2Minter
1043 self.adminStoragePath = /storage/TrmAssetV2_2Admin
1044
1045 // First, check to see if a minter resource already exists
1046
1047 if self.account.storage.type(at: self.minterStoragePath) == nil {
1048
1049 // Put the minter in storage with access only to admin
1050 self.account.storage.save(<-create Minter(), to: self.minterStoragePath)
1051 }
1052
1053 // First, check to see if a minter resource already exists
1054 if self.account.storage.type(at: self.adminStoragePath) == nil {
1055
1056 // Put the minter in storage with access only to admin
1057 self.account.storage.save(<-create Admin(), to: self.adminStoragePath)
1058 }
1059
1060 emit ContractInitialized()
1061 }
1062
1063 /// Function that returns all the Metadata Views implemented by a Non Fungible Token
1064 ///
1065 /// @return An array of Types defining the implemented views. This value will be used by
1066 /// developers to know which parameter to pass to the resolveView() method.
1067 ///
1068 access(all) view fun getContractViews(resourceType: Type?): [Type] {
1069 return [
1070 Type<MetadataViews.NFTCollectionDisplay>()
1071 ]
1072 }
1073 access(all) fun resolveContractView(resourceType: Type?,viewType: Type): AnyStruct? {
1074 // Check the type of the view requested and return the corresponding view
1075 switch viewType {
1076 case Type<MetadataViews.NFTCollectionDisplay>():
1077 return MetadataViews.Display(
1078 name: "Test",
1079 description: "Test ",
1080 thumbnail: MetadataViews.HTTPFile(
1081 url: ""
1082 )
1083 )
1084 }
1085
1086 // Implement additional views if necessary
1087 return nil
1088 }
1089
1090 /// Function that resolves a metadata view for this contract.
1091 ///
1092 /// @param view: The Type of the desired view.
1093 /// @return A structure representing the requested view.
1094 ///
1095
1096}
1097