Smart Contract

TrmAssetV2_2

A.61fc4b873e58733b.TrmAssetV2_2

Deployed

1w ago
Feb 15, 2026, 02:20:19 PM UTC

Dependents

29 imports
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