Smart Contract
Piece
A.fdc436fd7db22e01.Piece
1import FungibleToken from 0xf233dcee88fe0abe
2import FlowToken from 0x1654653399040a61
3import NonFungibleToken from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5import ViewResolver from 0x1d7e57aa55817448
6
7access(all) contract Piece: NonFungibleToken, ViewResolver {
8
9 // Collection Information
10 access(self) let collectionInfo: {String: AnyStruct}
11 // Contract Information
12 access(all) var totalSupply: UInt64
13 // Events
14 access(all) event ContractInitialized()
15 access(all) event Withdraw(id: UInt64, from: Address?)
16 access(all) event Deposit(id: UInt64, to: Address?)
17 access(all) event Minted(id: UInt64, serial: UInt64, recipient: Address, creatorID: UInt64)
18 access(all) event MetadataSuccess(creatorID: UInt64, description: String)
19 access(all) event MetadataError(error: String)
20 // Paths
21 access(all) let CollectionStoragePath: StoragePath
22 access(all) let CollectionPublicPath: PublicPath
23 access(all) let CollectionPrivatePath: PrivatePath
24 access(all) let AdministratorStoragePath: StoragePath
25 access(all) let MetadataStoragePath: StoragePath
26 access(all) let MetadataPublicPath: PublicPath
27
28 access(account) let nftStorage: @{Address: {UInt64: NFT}}
29
30 access(all) resource MetadataStorage: MetadataStoragePublic {
31 // List of Creator
32 access(all) var creatorsIds: {UInt64: [NFTMetadata]}
33
34 init () {
35 self.creatorsIds = {}
36 }
37 access(account) fun creatorExist(_ creatorId: UInt64) {
38 if self.creatorsIds[creatorId] == nil {
39 self.creatorsIds[creatorId] = []
40 }
41 }
42 access (account) fun metadataIsNew(_ creatorId: UInt64, _ description: String): Bool {
43 self.creatorExist(creatorId)
44 let metadata = self.findMetadata(creatorId, description)
45 if metadata == nil {
46 return true
47 } else {
48 return false
49 }
50 }
51 access(account) fun addMetadata(_ creatorId: UInt64,_ metadata: NFTMetadata) {
52
53 if self.creatorsIds[creatorId] == nil {
54 self.creatorsIds[creatorId] = []
55 }
56 self.creatorsIds[creatorId]?.append(metadata)!
57 }
58
59 access(account) fun updateMinted(_ creatorId: UInt64,_ description: String) {
60 let metadataRef = self.findMetadataRef(creatorId, description)!
61 metadataRef.updateMinted()
62 }
63 // Public Functions
64 access(account) fun findMetadataRef(_ creatorId: UInt64,_ description: String): &Piece.NFTMetadata? {
65 let metadatas = self.creatorsIds[creatorId]!
66 var i = metadatas.length - 1
67 while i >= 0 {
68 if (metadatas[i].description == description) {
69 let metadataRef: &Piece.NFTMetadata = (&self.creatorsIds[creatorId]![i] as &NFTMetadata)
70 return metadataRef
71 }
72 i = i - 1
73 }
74 return nil
75 }
76
77 // Public Functions
78 access(all) fun findMetadata(_ creatorId: UInt64,_ description: String): Piece.NFTMetadata? {
79 let metadatas = self.creatorsIds[creatorId]!
80 var i = metadatas.length - 1
81 while i >= 0 {
82 if (metadatas[i].description == description) {
83 return metadatas[i]
84 }
85 i = i - 1
86 }
87 return nil
88 }
89 access(all) fun getTimeRemaining(_ creatorID: UInt64,_ description: String): UFix64? {
90 let metadata = self.findMetadata(creatorID, description)!
91 let answer = (metadata.creationTime + 86400.0) - getCurrentBlock().timestamp
92 return answer
93 }
94 }
95
96 /// Defines the methods that are particular to this NFT contract collection
97 ///
98 access(all) resource interface MetadataStoragePublic {
99 access(all) fun getTimeRemaining(_ creatorID: UInt64,_ description: String): UFix64?
100 access(all) fun findMetadata(_ creatorId: UInt64,_ description: String): Piece.NFTMetadata?
101 }
102
103 access(all) struct NFTMetadata {
104 access(all) let creatorID: UInt64
105 access(all) var creatorUsername: String
106 access(all) let creatorAddress: Address
107 access(all) let description: String
108 access(all) let image: MetadataViews.HTTPFile
109 access(all) let metadataId: UInt64
110 access(all) var supply: UInt64
111 access(all) var minted: UInt64
112 access(all) let unlimited: Bool
113 access(all) var extra: {String: AnyStruct}
114 access(all) var timer: UInt64
115 access(all) let pieceCreationDate: String
116 access(all) let contentCreationDate: String
117 access(all) let creationTime: UFix64
118 access(all) let lockdownTime: UFix64
119 access(all) let embededHTML: String
120
121 access(account) fun updateMinted() {
122 self.minted = self.minted + 1
123 if(self.unlimited) {
124 self.supply = self.supply + 1
125 }
126 }
127 init(
128 _creatorID: UInt64,
129 _creatorUsername: String,
130 _creatorAddress: Address,
131 _description: String,
132 _image: MetadataViews.HTTPFile,
133 _supply: UInt64,
134 _extra: {String: AnyStruct},
135 _pieceCreationDate: String,
136 _contentCreationDate: String,
137 _currentTime: UFix64,
138 _lockdownTime: UFix64,
139 _embededHTML: String,
140 ) {
141
142 self.metadataId = _creatorID
143 self.creatorID = _creatorID
144 self.creatorUsername = _creatorUsername
145 self.creatorAddress = _creatorAddress
146 self.description = _description
147 self.image = _image
148 self.extra = _extra
149 self.supply = _supply
150 self.unlimited = _supply == 0
151 self.minted = 0
152 self.timer = 0
153 self.pieceCreationDate = _pieceCreationDate
154 self.contentCreationDate = _contentCreationDate
155 self.creationTime = _currentTime
156 self.lockdownTime = _lockdownTime
157 self.embededHTML = _embededHTML
158 }
159 }
160
161 /// We choose the name NFT here, but this type can have any name now
162 /// could be changed to PieceNFT
163 access(all) resource NFT: NonFungibleToken.NFT {
164 access(all) let id: UInt64
165 // The 'metadataId' is what maps this NFT to its 'NFTMetadata'
166 access(all) let creatorID: UInt64
167 access(all) let serial: UInt64
168 access(all) let description: String
169 access(all) let originalMinter: Address
170
171 init(_creatorID: UInt64, _description: String, _recipient: Address) {
172
173 // Fetch the metadata blueprint
174 let metadatas = Piece.account.storage.borrow<&Piece.MetadataStorage>(from: Piece.MetadataStoragePath)!
175 let metadataRef = metadatas.findMetadata(_creatorID, _description)!
176 // Assign serial number to the NFT based on the number of minted NFTs
177 self.id = self.uuid
178 self.creatorID = _creatorID
179 self.serial = metadataRef.supply
180 self.description = _description
181 self.originalMinter = _recipient
182 // Update the total supply of this MetadataId by 1
183 metadatas.updateMinted(_creatorID, _description)
184 // Update Piece collection NFTs count
185 Piece.totalSupply = Piece.totalSupply + 1
186 emit Minted(id: self.id, serial: self.serial, recipient: _recipient, creatorID: _creatorID)
187 }
188
189 access(all) fun getMetadata(): NFTMetadata {
190 return Piece.getMetadata(self.creatorID, self.description )!
191 }
192 /// createEmptyCollection creates an empty Collection
193 /// and returns it to the caller so that they can own NFTs
194 /// @{NonFungibleToken.Collection}
195 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
196 return <-Piece.createEmptyCollection(nftType: Type<@Piece.NFT>())
197 }
198
199 access(all) view fun getViews(): [Type] {
200 return [
201 Type<MetadataViews.Display>(),
202 Type<MetadataViews.Royalties>(),
203 Type<MetadataViews.Editions>(),
204 Type<MetadataViews.ExternalURL>(),
205 Type<MetadataViews.NFTCollectionData>(),
206 Type<MetadataViews.NFTCollectionDisplay>(),
207 Type<MetadataViews.Serial>(),
208 Type<MetadataViews.Traits>(),
209 Type<MetadataViews.EVMBridgedMetadata>()
210 ]
211 }
212
213 access(all) fun resolveView(_ view: Type): AnyStruct? {
214 let metadata = self.getMetadata()
215 switch view {
216 case Type<MetadataViews.Display>():
217 return MetadataViews.Display(
218 name: metadata.creatorUsername.concat(" ").concat(metadata.contentCreationDate),
219 description: metadata.description,
220 thumbnail: metadata.image
221 )
222 case Type<MetadataViews.Traits>():
223/* let metaCopy = metadata.extra
224 metaCopy["Serial"] = self.serial */
225 return MetadataViews.dictToTraits(dict: {"String": 2}, excludedNames: nil)
226
227 case Type<MetadataViews.NFTView>():
228 return MetadataViews.NFTView(
229 id: self.id,
230 uuid: self.uuid,
231 display: self.resolveView(Type<MetadataViews.Display>()) as! MetadataViews.Display?,
232 externalURL: self.resolveView(Type<MetadataViews.ExternalURL>()) as! MetadataViews.ExternalURL?,
233 collectionData: self.resolveView(Type<MetadataViews.NFTCollectionData>()) as! MetadataViews.NFTCollectionData?,
234 collectionDisplay: self.resolveView(Type<MetadataViews.NFTCollectionDisplay>()) as! MetadataViews.NFTCollectionDisplay?,
235 royalties: self.resolveView(Type<MetadataViews.Royalties>()) as! MetadataViews.Royalties?,
236 traits: self.resolveView(Type<MetadataViews.Traits>()) as! MetadataViews.Traits?
237 )
238 case Type<MetadataViews.NFTCollectionData>():
239 return Piece.resolveContractView(resourceType: Type<@Piece.NFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
240 case Type<MetadataViews.ExternalURL>():
241 return Piece.getCollectionAttribute(key: "website") as! MetadataViews.ExternalURL
242 case Type<MetadataViews.NFTCollectionDisplay>():
243 return Piece.resolveContractView(resourceType: Type<@Piece.NFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>())
244 case Type<MetadataViews.Medias>():
245 let metadata = 10
246 if metadata != nil {
247 return MetadataViews.Medias(
248 [
249 MetadataViews.Media(
250 file: MetadataViews.HTTPFile(
251 url: "metadata.embededHTML"
252 ),
253 mediaType: "html"
254 )
255 ]
256 )
257 }
258 case Type<MetadataViews.Royalties>():
259 return MetadataViews.Royalties([
260 MetadataViews.Royalty(
261 receiver: getAccount(Piece.account.address).capabilities.get<&FlowToken.Vault>(/public/flowTokenReceiver),
262 cut: 0.10, // 10% royalty on secondary sales
263 description: "The creator of the original content gets 10% of every secondary sale."
264 )
265 ])
266 case Type<MetadataViews.Serial>():
267 return MetadataViews.Serial(
268 self.serial
269 )
270 }
271 return nil
272 }
273
274 }
275
276 /// Defines the methods that are particular to this NFT contract collection
277 ///
278 access(all) resource interface PieceCollectionPublic {
279 access(all) fun deposit(token: @{NonFungibleToken.NFT})
280 access(all) fun getIDs(): [UInt64]
281 // access(all) fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
282
283/* access(all) fun borrowPiece(id: UInt64): &Piece.NFT? {
284 post {
285 (result == nil) || (result?.id == id):
286 "Cannot borrow Piece NFT reference: the ID of the returned reference is incorrect"
287 }
288 } */
289 }
290
291 access(all) resource Collection: PieceCollectionPublic, NonFungibleToken.Collection {
292 // *** Collection Variables *** //
293 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
294 // *** Collection Constructor *** //
295 init () {
296 self.ownedNFTs <- {}
297 }
298 // *** Collection Functions *** //
299
300 /// Returns a list of NFT types that this receiver accepts
301 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
302 let supportedTypes: {Type: Bool} = {}
303 supportedTypes[Type<@Piece.NFT>()] = true
304 return supportedTypes
305 }
306 /// Returns whether or not the given type is accepted by the collection
307 /// A collection that can accept any type should just return true by default
308 access(all) view fun isSupportedNFTType(type: Type): Bool {
309 return type == Type<@Piece.NFT>()
310 }
311 // Withdraw removes a PieceNFT from the collection and moves it to the caller(for Trading)
312 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
313 let token <- self.ownedNFTs.remove(key: withdrawID)
314 ?? panic("This Collection doesn't own a PieceNFT by id: ".concat(withdrawID.toString()))
315
316 emit Withdraw(id: token.id, from: self.owner?.address)
317
318 return <-token
319 }
320 // Deposit takes a PieceNFT and adds it to the collections dictionary
321 // and adds the ID to the id array
322 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
323 let newPiece <- token as! @NFT
324 let id: UInt64 = newPiece.id
325 // Add the new PieceNFT to the dictionary
326 let oldPiece <- self.ownedNFTs[id] <- newPiece
327 // Destroy old Piece in that slot
328 destroy oldPiece
329
330 emit Deposit(id: id, to: self.owner?.address)
331 }
332
333 // GetIDs returns an array of the IDs that are in the collection
334 access(all) view fun getIDs(): [UInt64] {
335 return self.ownedNFTs.keys
336 }
337 /// Gets the amount of NFTs stored in the collection
338 access(all) view fun getLength(): Int {
339 return self.ownedNFTs.length
340 }
341
342 // BorrowNFT gets a reference to an NFT in the collection
343 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
344 return &self.ownedNFTs[id]
345 }
346
347 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
348 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
349 return nft as &{ViewResolver.Resolver}
350 }
351 return nil
352 }
353 /// createEmptyCollection creates an empty Collection of the same type
354 /// and returns it to the caller
355 /// @return A an empty collection of the same type
356 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
357 return <-Piece.createEmptyCollection(nftType: Type<@Piece.NFT>())
358 }
359
360 /// Gets a reference to an NFT in the collection so that
361 /// the caller can read its metadata and call its methods
362 ///
363 /// @param id: The ID of the wanted NFT
364 /// @return A reference to the wanted NFT resource
365 ///
366/* access(all) fun borrowPiece(id: UInt64): &Piece.NFT? {
367 if self.ownedNFTs[id] != nil {
368 // Create an authorized reference to allow downcasting
369 let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
370 return ref as! &Piece.NFT
371 }
372
373 return nil
374 } */
375
376
377/* access(all) fun claim() {
378 if let storage = &Piece.nftStorage[self.owner!.address] as &{UInt64: NFT}? {
379 for id in storage.keys {
380 self.deposit(token: <- storage.remove(key: id)!)
381 }
382 }
383 } */
384 }
385
386 access(all) resource Administrator {
387 // Function to upload the Metadata to the contract.
388 access(all) fun createNFTMetadata(
389 channel: String,
390 creatorID: UInt64,
391 creatorUsername: String,
392 creatorAddress: Address,
393 sourceURL: String,
394 description: String,
395 pieceCreationDate: String,
396 contentCreationDate: String,
397 lockdownOption: Int,
398 supplyOption: UInt64,
399 imgUrl: String,
400 embededHTML: String)
401 {
402 // Load the metadata from the Piece account
403 let metadatas = Piece.account.storage.borrow<&Piece.MetadataStorage>(from: Piece.MetadataStoragePath)!
404 // Check if Metadata already exist
405 if metadatas.metadataIsNew(creatorID, description) {
406 metadatas.addMetadata(creatorID, NFTMetadata(
407 _creatorID: creatorID,
408 _creatorUsername: creatorUsername,
409 _creatorAddress: creatorAddress,
410 _description: description,
411 _image: MetadataViews.HTTPFile(
412 url: imgUrl,
413 ),
414 _supply: supplyOption,
415 _extra: {
416 "Creator username": creatorUsername,
417 "Creator ID": creatorID,
418 "Channel": channel,
419 "Text content": description,
420 "Source": sourceURL,
421 "Piece creation date": pieceCreationDate,
422 "Content creation date": contentCreationDate
423 },
424 _pieceCreationDate: pieceCreationDate,
425 _contentCreationDate: contentCreationDate,
426 _currentTime: getCurrentBlock().timestamp,
427 _lockdownTime: self.getLockdownTime(lockdownOption),
428 _embededHTML: embededHTML,
429 ))
430 emit MetadataSuccess(creatorID: creatorID, description: description)
431 } else {
432 emit MetadataError(error: "A Metadata for this Event already exist")
433 }
434 // Piece.account.save(<- metadatas, to: Piece.MetadataStoragePath)
435 }
436
437 // mintNFT mints a new NFT and deposits
438 // it in the recipients collection
439 access(all) fun mintNFT(creatorId: UInt64, description: String, recipient: Address) {
440 pre {
441 self.isMintingAvailable(creatorId, description): "Minting for this NFT has ended or reached max supply."
442 }
443
444 let nft <- create NFT(_creatorID: creatorId, _description: description, _recipient: recipient)
445
446 if let recipientCollection = getAccount(recipient)
447 .capabilities.borrow<&{NonFungibleToken.Receiver}>(Piece.CollectionPublicPath)
448 {
449 recipientCollection.deposit(token: <- nft)
450 } else {
451 destroy nft
452/* if let storage = &Piece.nftStorage[recipient] as &{UInt64: NFT}? {
453 storage[nft.id] <-! nft
454 } else {
455 Piece.nftStorage[recipient] <-! {nft.id: <- nft}
456 } */
457 }
458 }
459
460 // create a new Administrator resource
461 access(all) fun createAdmin(): @Administrator {
462 return <- create Administrator()
463 }
464 // change piece of collection info
465 access(all) fun changeField(key: String, value: AnyStruct) {
466 Piece.collectionInfo[key] = value
467 }
468
469 access(account) view fun isMintingAvailable(_ creatorId: UInt64, _ description: String): Bool {
470 return true
471 }
472
473 access(account) fun getLockdownTime(_ lockdownOption: Int): UFix64 {
474 switch lockdownOption {
475 case 0:
476 return 21600.0
477 case 1:
478 return 43200.0
479 case 2:
480 return 604800.0
481 case 3:
482 return 172800.0
483 default:
484 return 0.0
485 }
486 }
487 }
488
489 /// createEmptyCollection creates an empty Collection for the specified NFT type
490 /// and returns it to the caller so that they can own NFTs
491 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
492 return <- create Collection()
493 }
494
495 // Get information about a NFTMetadata
496 access(all) fun getMetadata(_ creatorId: UInt64,_ description: String): Piece.NFTMetadata? {
497 let storage = Piece.account.storage.borrow<&{Piece.MetadataStoragePublic}>(from: Piece.MetadataStoragePath)!
498 let metadata = storage.findMetadata(creatorId, description)
499 return metadata
500 }
501
502 access(all) fun getCollectionInfo(): {String: AnyStruct} {
503 let collectionInfo = self.collectionInfo
504 collectionInfo["totalSupply"] = self.totalSupply
505 collectionInfo["version"] = 1
506 return collectionInfo
507 }
508
509 access(all) fun getCollectionAttribute(key: String): AnyStruct {
510 return self.collectionInfo[key] ?? panic(key.concat(" is not an attribute in this collection."))
511 }
512
513 /// Function that returns all the Metadata Views implemented by a Non Fungible Token
514 ///
515 /// @return An array of Types defining the implemented views. This value will be used by
516 /// developers to know which parameter to pass to the resolveView() method.
517 ///
518 access(all) view fun getContractViews(resourceType: Type?): [Type] {
519 return [
520 Type<MetadataViews.NFTCollectionData>(),
521 Type<MetadataViews.NFTCollectionDisplay>(),
522 Type<MetadataViews.EVMBridgedMetadata>()
523 ]
524 }
525 /// Function that resolves a metadata view for this contract.
526 ///
527 /// @param view: The Type of the desired view.
528 /// @return A structure representing the requested view.
529 ///
530 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
531 switch viewType {
532 case Type<MetadataViews.NFTCollectionData>():
533 let collectionData = MetadataViews.NFTCollectionData(
534 storagePath: self.CollectionStoragePath,
535 publicPath: self.CollectionPublicPath,
536 publicCollection: Type<&Piece.Collection>(),
537 publicLinkedType: Type<&Piece.Collection>(),
538 createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
539 return <-Piece.createEmptyCollection(nftType: Type<@Piece.NFT>())
540 })
541 )
542 return collectionData
543 case Type<MetadataViews.NFTCollectionDisplay>():
544 let media = Piece.getCollectionAttribute(key: "image") as! MetadataViews.Media
545 return MetadataViews.NFTCollectionDisplay(
546 name: "Piece",
547 description: "Sell Pieces of any Tweet in seconds.",
548 externalURL: MetadataViews.ExternalURL("https://piece.gg/"),
549 squareImage: media,
550 bannerImage: media,
551 socials: {
552 "twitter": MetadataViews.ExternalURL("https://twitter.com/CreateAPiece")
553 }
554 )
555
556/* case Type<MetadataViews.EVMBridgedMetadata>():
557 // Implementing this view gives the project control over how the bridged NFT is represented as an ERC721
558 // when bridged to EVM on Flow via the public infrastructure bridge.
559
560 // Compose the contract-level URI. In this case, the contract metadata is located on some HTTP host,
561 // but it could be IPFS, S3, a data URL containing the JSON directly, etc.
562 return MetadataViews.EVMBridgedMetadata(
563 name: "ExampleNFT",
564 symbol: "XMPL",
565 uri: MetadataViews.URI(
566 baseURI: nil, // setting baseURI as nil sets the given value as the uri field value
567 value: "https://example-nft.onflow.org/contract-metadata.json"
568 )
569 ) */
570 // case Type<MetadataViews.EVMBridgedMetadata>():
571 // return MetadataViews.EVMBridgedMetadata(
572 // name: self.name,
573 // symbol: "XMPL",
574 // uri: MetadataViews.URI(
575 // baseURI: nil,
576 // value: SerializeMetadata.serializeNFTMetadataAsURI(&self as &{NonFungibleToken.NFT})
577 // )
578 // )
579 }
580 return nil
581 }
582
583 init() {
584 // Collection Info
585 self.collectionInfo = {}
586 self.collectionInfo["name"] = "Piece"
587 self.collectionInfo["description"] = "Sell Pieces of any Tweet in seconds."
588 self.collectionInfo["image"] = MetadataViews.Media(
589 file: MetadataViews.HTTPFile(
590 url: "https://www.piece.gg/static/media/logo.48da6adac82863dd4955abe125b5c8dd.svg"
591 ),
592 mediaType: "image/jpeg"
593 )
594 self.collectionInfo["dateCreated"] = getCurrentBlock().timestamp
595 self.collectionInfo["website"] = MetadataViews.ExternalURL("https://www.piece.gg/")
596 self.collectionInfo["socials"] = {"Twitter": MetadataViews.ExternalURL("https://frontend-react-git-testing-piece.vercel.app/")}
597 self.totalSupply = 0
598 self.nftStorage <- {}
599
600 let identifier = "Piece_Collection".concat(self.account.address.toString())
601
602 // Set the named paths
603 self.CollectionStoragePath = StoragePath(identifier: identifier)!
604 self.CollectionPublicPath = PublicPath(identifier: identifier)!
605 self.CollectionPrivatePath = PrivatePath(identifier: identifier)!
606 self.AdministratorStoragePath = StoragePath(identifier: identifier.concat("_Administrator"))!
607 self.MetadataStoragePath = StoragePath(identifier: identifier.concat("_Metadata"))!
608 self.MetadataPublicPath = PublicPath(identifier: identifier.concat("_Metadata"))!
609
610 // Create a Collection resource and save it to storage
611 let collection <- create Collection()
612 self.account.storage.save(<- collection, to: self.CollectionStoragePath)
613 // create a public capability for the collection
614 let collectionCap = self.account.capabilities.storage.issue<&Piece.Collection>(self.CollectionStoragePath)
615 self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
616 // Create a Administrator resource and save it to Piece account storage
617 let administrator <- create Administrator()
618 self.account.storage.save(<- administrator, to: self.AdministratorStoragePath)
619
620 // Create a Metadata Storage resource and save it to Piece account storage
621 let metadataStorage <- create MetadataStorage()
622 self.account.storage.save(<- metadataStorage, to: self.MetadataStoragePath)
623
624 emit ContractInitialized()
625 }
626}