Smart Contract
AtlantaNft_NFT
A.329feb3ab062d289.AtlantaNft_NFT
1
2import FungibleToken from 0xf233dcee88fe0abe
3import NonFungibleToken from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5import ViewResolver from 0x1d7e57aa55817448
6
7access(all) contract AtlantaNft_NFT: NonFungibleToken {
8
9 // An entitlement to perform Admin actions on this contract
10 access(all) entitlement Administrator
11
12 // AtlantaNft_NFT Events
13 //
14
15 // Emitted when an NFT is minted
16 access(all) event Minted(id: UInt64, setId: UInt32, seriesId: UInt32)
17
18 // Events for Series-related actions
19 //
20 // Emitted when a new Series is created
21 access(all) event SeriesCreated(seriesId: UInt32)
22 // Emitted when a Series is sealed, meaning Series metadata
23 // cannot be updated
24 access(all) event SeriesSealed(seriesId: UInt32)
25 // Emitted when a Series' metadata is updated
26 access(all) event SeriesMetadataUpdated(seriesId: UInt32)
27
28 // Events for Set-related actions
29 //
30 // Emitted when a new Set is created
31 access(all) event SetCreated(seriesId: UInt32, setId: UInt32)
32 // Emitted when a Set's metadata is updated
33 access(all) event SetMetadataUpdated(seriesId: UInt32, setId: UInt32)
34
35 // Named Paths
36 //
37 access(all) let CollectionStoragePath: StoragePath
38 access(all) let CollectionPublicPath: PublicPath
39 access(all) let AdminStoragePath: StoragePath
40
41 // Variable size dictionary of SetData structs
42 access(self) var setData: {UInt32: NFTSetData}
43
44 // Variable size dictionary of SeriesData structs
45 access(self) var seriesData: {UInt32: SeriesData}
46
47 // Variable size dictionary of Series resources
48 access(self) var series: @{UInt32: Series}
49
50
51 // An NFTSetData is a Struct that holds metadata associated with
52 // a specific NFT Set.
53 access(all) struct NFTSetData {
54
55 // Unique ID for the Set
56 access(all) let setId: UInt32
57
58 // Series ID the Set belongs to
59 access(all) let seriesId: UInt32
60
61 // Maximum number of editions that can be minted in this Set
62 access(all) let maxEditions: UInt32
63
64 // The JSON metadata for each NFT edition can be stored off-chain on IPFS.
65 // This is an optional dictionary of IPFS hashes, which will allow marketplaces
66 // to pull the metadata for each NFT edition
67 access(self) var ipfsMetadataHashes: {UInt32: String}
68
69 // Set level metadata
70 // Dictionary of metadata key value pairs
71 access(self) var metadata: {String: String}
72
73 init(
74 setId: UInt32,
75 seriesId: UInt32,
76 maxEditions: UInt32,
77 ipfsMetadataHashes: {UInt32: String},
78 metadata: {String: String}) {
79
80 self.setId = setId
81 self.seriesId = seriesId
82 self.maxEditions = maxEditions
83 self.metadata = metadata
84 self.ipfsMetadataHashes = ipfsMetadataHashes
85 }
86
87 access(all) fun getIpfsMetadataHash(editionNum: UInt32): String? {
88 return self.ipfsMetadataHashes[editionNum]
89 }
90
91 access(all) fun getMetadata(): {String: String} {
92 return self.metadata
93 }
94
95 access(all) fun getMetadataField(field: String): String? {
96 return self.metadata[field]
97 }
98 }
99
100 // A SeriesData is a struct that groups metadata for a
101 // a related group of NFTSets.
102 access(all) struct SeriesData {
103
104 // Unique ID for the Series
105 access(all) let seriesId: UInt32
106
107 // Dictionary of metadata key value pairs
108 access(self) var metadata: {String: String}
109
110 init(
111 seriesId: UInt32,
112 metadata: {String: String}) {
113 self.seriesId = seriesId
114 self.metadata = metadata
115 }
116
117 access(all) fun getMetadata(): {String: String} {
118 return self.metadata
119 }
120 }
121
122
123 // A Series is special resource type that contains functions to mint AtlantaNft_NFT NFTs,
124 // add NFTSets, update NFTSet and Series metadata, and seal Series.
125 access(all) resource Series {
126
127 // Unique ID for the Series
128 access(all) let seriesId: UInt32
129
130 // Array of NFTSets that belong to this Series
131 access(all) var setIds: [UInt32]
132
133 // Series sealed state
134 access(all) var seriesSealedState: Bool;
135
136 // Set sealed state
137 access(self) var setSealedState: {UInt32: Bool};
138
139 // Current number of editions minted per Set
140 access(all) var numberEditionsMintedPerSet: {UInt32: UInt32}
141
142 init(
143 seriesId: UInt32,
144 metadata: {String: String}) {
145
146 self.seriesId = seriesId
147 self.seriesSealedState = false
148 self.numberEditionsMintedPerSet = {}
149 self.setIds = []
150 self.setSealedState = {}
151
152 AtlantaNft_NFT.seriesData[seriesId] = SeriesData(
153 seriesId: seriesId,
154 metadata: metadata
155 )
156
157 emit SeriesCreated(seriesId: seriesId)
158 }
159
160 access(all) fun addNftSet(
161 setId: UInt32,
162 maxEditions: UInt32,
163 ipfsMetadataHashes: {UInt32: String},
164 metadata: {String: String}) {
165 pre {
166 self.setIds.contains(setId) == false: "The Set has already been added to the Series."
167 }
168
169 // Create the new Set struct
170 var newNFTSet = NFTSetData(
171 setId: setId,
172 seriesId: self.seriesId,
173 maxEditions: maxEditions,
174 ipfsMetadataHashes: ipfsMetadataHashes,
175 metadata: metadata
176 )
177
178 // Add the NFTSet to the array of Sets
179 self.setIds.append(setId)
180
181 // Initialize the NFT edition count to zero
182 self.numberEditionsMintedPerSet[setId] = 0
183
184 // Store it in the sets mapping field
185 AtlantaNft_NFT.setData[setId] = newNFTSet
186
187 emit SetCreated(seriesId: self.seriesId, setId: setId)
188 }
189
190 // updateSeriesMetadata
191 // For practical reasons, a short period of time is given to update metadata
192 // following Series creation or minting of the NFT editions. Once the Series is
193 // sealed, no updates to the Series metadata will be possible - the information
194 // is permanent and immutable.
195 access(all) fun updateSeriesMetadata(metadata: {String: String}) {
196 pre {
197 self.seriesSealedState == false:
198 "The Series is permanently sealed. No metadata updates can be made."
199 }
200 let newSeriesMetadata = SeriesData(
201 seriesId: self.seriesId,
202 metadata: metadata
203 )
204 // Store updated Series in the Series mapping field
205 AtlantaNft_NFT.seriesData[self.seriesId] = newSeriesMetadata
206
207 emit SeriesMetadataUpdated(seriesId: self.seriesId)
208 }
209
210 // updateSetMetadata
211 // For practical reasons, a short period of time is given to update metadata
212 // following Set creation or minting of the NFT editions. Once the Series is
213 // sealed, no updates to the Set metadata will be possible - the information
214 // is permanent and immutable.
215 access(all) fun updateSetMetadata(
216 setId: UInt32,
217 maxEditions: UInt32,
218 ipfsMetadataHashes: {UInt32: String},
219 metadata: {String: String}) {
220 pre {
221 self.seriesSealedState == false:
222 "The Series is permanently sealed. No metadata updates can be made."
223 self.setIds.contains(setId) == true: "The Set is not part of this Series."
224 }
225 let newSetMetadata = NFTSetData(
226 setId: setId,
227 seriesId: self.seriesId,
228 maxEditions: maxEditions,
229 ipfsMetadataHashes: ipfsMetadataHashes,
230 metadata: metadata
231 )
232 // Store updated Set in the Sets mapping field
233 AtlantaNft_NFT.setData[setId] = newSetMetadata
234
235 emit SetMetadataUpdated(seriesId: self.seriesId, setId: setId)
236 }
237
238 // mintAtlantaNft_NFT
239 // Mints a new NFT with a new ID
240 // and deposits it in the recipients collection using their collection reference
241 //
242 access(all) fun mintAtlantaNft_NFT(
243 recipient: &{NonFungibleToken.CollectionPublic},
244 tokenId: UInt64,
245 setId: UInt32) {
246
247 pre {
248 self.numberEditionsMintedPerSet[setId] != nil: "The Set does not exist."
249 self.numberEditionsMintedPerSet[setId]! <= AtlantaNft_NFT.getSetMaxEditions(setId: setId)!:
250 "Set has reached maximum NFT edition capacity."
251 }
252
253 // Gets the number of editions that have been minted so far in
254 // this set
255 let editionNum: UInt32 = self.numberEditionsMintedPerSet[setId]! + 1
256
257 // deposit it in the recipient's account using their reference
258 recipient.deposit(token: <-create AtlantaNft_NFT.NFT(
259 tokenId: tokenId,
260 setId: setId,
261 editionNum: editionNum
262 ))
263
264 // Update the count of Editions minted in the set
265 self.numberEditionsMintedPerSet[setId] = editionNum
266 }
267
268 // mintEditionAtlantaNft_NFT
269 // Mints a new NFT with a new ID and specific edition Num (random open edition)
270 // and deposits it in the recipients collection using their collection reference
271 //
272 access(all) fun mintEditionAtlantaNft_NFT(
273 recipient: &{NonFungibleToken.CollectionPublic},
274 tokenId: UInt64,
275 setId: UInt32,
276 edition: UInt32) {
277
278 pre {
279 self.numberEditionsMintedPerSet[setId] != nil: "The Set does not exist."
280 self.numberEditionsMintedPerSet[setId]! <= AtlantaNft_NFT.getSetMaxEditions(setId: setId)!:
281 "Set has reached maximum NFT edition capacity."
282 }
283
284 // deposit it in the recipient's account using their reference
285 recipient.deposit(token: <-create AtlantaNft_NFT.NFT(
286 tokenId: tokenId,
287 setId: setId,
288 editionNum: edition
289 ))
290
291 // Update the count of Editions minted in the set
292 self.numberEditionsMintedPerSet[setId] = self.numberEditionsMintedPerSet[setId]! + 1
293 }
294
295 // batchMintAtlantaNft_NFT
296 // Mints multiple new NFTs given and deposits the NFTs
297 // into the recipients collection using their collection reference
298 access(all) fun batchMintAtlantaNft_NFT(
299 recipient: &{NonFungibleToken.CollectionPublic},
300 setId: UInt32,
301 tokenIds: [UInt64]) {
302
303 pre {
304 tokenIds.length > 0:
305 "Number of token Ids must be > 0"
306 }
307
308 for tokenId in tokenIds {
309 self.mintAtlantaNft_NFT(
310 recipient: recipient,
311 tokenId: tokenId,
312 setId: setId
313 )
314 }
315 }
316
317 // sealSeries
318 // Once a series is sealed, the metadata for the NFTs in the Series can no
319 // longer be updated
320 //
321 access(all) fun sealSeries() {
322 pre {
323 self.seriesSealedState == false: "The Series is already sealed"
324 }
325 self.seriesSealedState = true
326
327 emit SeriesSealed(seriesId: self.seriesId)
328 }
329 }
330
331 // A resource that represents the AtlantaNft_NFT NFT
332 //
333 access(all) resource NFT: NonFungibleToken.NFT {
334 // The token's ID
335 access(all) let id: UInt64
336
337 // The Set id references this NFT belongs to
338 access(all) let setId: UInt32
339
340 // The specific edition number for this NFT
341 access(all) let editionNum: UInt32
342
343 // initializer
344 //
345 init(
346 tokenId: UInt64,
347 setId: UInt32,
348 editionNum: UInt32) {
349
350 self.id = tokenId
351 self.setId = setId
352 self.editionNum = editionNum
353
354 let seriesId = AtlantaNft_NFT.getSetSeriesId(setId: setId)!
355
356 emit Minted(id: self.id, setId: setId, seriesId: seriesId)
357 }
358
359 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
360 return <- AtlantaNft_NFT.createEmptyCollection(nftType: Type<@AtlantaNft_NFT.NFT>())
361 }
362
363 access(all) view fun getViews(): [Type] {
364 return [
365 Type<MetadataViews.Display>(),
366 Type<MetadataViews.Royalties>(),
367 Type<MetadataViews.Editions>(),
368 Type<MetadataViews.ExternalURL>(),
369 Type<MetadataViews.NFTCollectionData>(),
370 Type<MetadataViews.NFTCollectionDisplay>(),
371 Type<MetadataViews.Serial>(),
372 Type<MetadataViews.Traits>(),
373 Type<MetadataViews.Medias>()
374 ]
375 }
376 access(all) fun resolveView(_ view: Type): AnyStruct? {
377 switch view {
378 case Type<MetadataViews.Display>():
379 return MetadataViews.Display(
380 name: AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "name")!,
381 description: AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "description")!,
382 thumbnail: MetadataViews.HTTPFile(
383 url: AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "preview")!
384 )
385 )
386 case Type<MetadataViews.Serial>():
387 return MetadataViews.Serial(
388 self.id
389 )
390 case Type<MetadataViews.Editions>():
391 let maxEditions = AtlantaNft_NFT.setData[self.setId]?.maxEditions ?? 0
392 let editionName = AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "name")!
393 let editionInfo = MetadataViews.Edition(name: editionName, number: UInt64(self.editionNum), max: maxEditions > 0 ? UInt64(maxEditions) : nil)
394 let editionList: [MetadataViews.Edition] = [editionInfo]
395 return MetadataViews.Editions(
396 editionList
397 )
398 case Type<MetadataViews.ExternalURL>():
399 if let externalBaseURL = AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "external_token_base_url") {
400 return MetadataViews.ExternalURL(externalBaseURL.concat("/").concat(self.id.toString()))
401 }
402 return MetadataViews.ExternalURL("")
403 case Type<MetadataViews.Royalties>():
404 let royalties: [MetadataViews.Royalty] = []
405 // There is only a legacy {String: String} dictionary to store royalty information.
406 // There may be multiple royalty cuts defined per NFT. Pull each royalty
407 // based on keys that have the "royalty_addr_" prefix in the dictionary.
408 for metadataKey in AtlantaNft_NFT.getSetMetadata(setId: self.setId)!.keys {
409 // For efficiency, only check keys that are > 13 chars, which is the length of "royalty_addr_" key
410 if metadataKey.length >= 13 {
411 if metadataKey.slice(from: 0, upTo: 13) == "royalty_addr_" {
412 // A royalty has been found. Use the suffix from the key for the royalty name.
413 let royaltyName = metadataKey.slice(from: 13, upTo: metadataKey.length)
414 let royaltyAddress = AtlantaNft_NFT.convertStringToAddress(AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_addr_".concat(royaltyName))!)!
415 let royaltyReceiver: PublicPath = PublicPath(identifier: AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_rcv_".concat(royaltyName))!)!
416 let royaltyCut = AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_cut_".concat(royaltyName))!
417 let cutValue: UFix64 = AtlantaNft_NFT.royaltyCutStringToUFix64(royaltyCut)
418 if cutValue != 0.0 {
419 royalties.append(MetadataViews.Royalty(
420 receiver: getAccount(royaltyAddress).capabilities.get<&{FungibleToken.Receiver}>(royaltyReceiver),
421 cut: cutValue,
422 description: AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "royalty_desc_".concat(royaltyName))!
423 )
424 )
425 }
426 }
427 }
428 }
429 return MetadataViews.Royalties(royalties)
430 case Type<MetadataViews.NFTCollectionData>():
431 return AtlantaNft_NFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>())
432 case Type<MetadataViews.NFTCollectionDisplay>():
433 return AtlantaNft_NFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionDisplay>())
434 case Type<MetadataViews.Traits>():
435 let traitDictionary: {String: AnyStruct} = {}
436 // There is only a legacy {String: String} dictionary to store trait information.
437 // There may be multiple traits defined per NFT. Pull trait information
438 // based on keys that have the "trait_" prefix in the dictionary.
439 for metadataKey in AtlantaNft_NFT.getSetMetadata(setId: self.setId)!.keys {
440 // For efficiency, only check keys that are > 6 chars, which is the length of "trait_" key
441 if metadataKey.length >= 6 {
442 if metadataKey.slice(from: 0, upTo: 6) == "trait_" {
443 // A trait has been found. Set the trait name to only the trait key suffix.
444 traitDictionary.insert(key: metadataKey.slice(from: 6, upTo: metadataKey.length), AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: metadataKey)!)
445 }
446 }
447 }
448 return MetadataViews.dictToTraits(dict: traitDictionary, excludedNames: [])
449 case Type<MetadataViews.Medias>():
450 return MetadataViews.Medias(
451 [
452 MetadataViews.Media(
453 file: MetadataViews.HTTPFile(
454 url: AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "image")!
455 ),
456 mediaType: self.getMimeType()
457 )
458 ]
459 )
460 }
461 return nil
462 }
463
464 access(all) fun getMimeType(): String {
465 var metadataFileType = AtlantaNft_NFT.getSetMetadataByField(setId: self.setId, field: "image_file_type")!.toLower()
466 switch metadataFileType {
467 case "mp4":
468 return "video/mp4"
469 case "mov":
470 return "video/quicktime"
471 case "webm":
472 return "video/webm"
473 case "ogv":
474 return "video/ogg"
475 case "png":
476 return "image/png"
477 case "jpeg":
478 return "image/jpeg"
479 case "jpg":
480 return "image/jpeg"
481 case "gif":
482 return "image/gif"
483 case "webp":
484 return "image/webp"
485 case "svg":
486 return "image/svg+xml"
487 case "glb":
488 return "model/gltf-binary"
489 case "gltf":
490 return "model/gltf+json"
491 case "obj":
492 return "model/obj"
493 case "mtl":
494 return "model/mtl"
495 case "mp3":
496 return "audio/mpeg"
497 case "ogg":
498 return "audio/ogg"
499 case "oga":
500 return "audio/ogg"
501 case "wav":
502 return "audio/wav"
503 case "html":
504 return "text/html"
505 }
506 return ""
507 }
508 }
509
510 // Admin is a special authorization resource that
511 // allows the owner to perform important NFT
512 // functions
513 //
514 access(all) resource Admin {
515
516 access(all) fun addSeries(seriesId: UInt32, metadata: {String: String}) {
517 pre {
518 AtlantaNft_NFT.series[seriesId] == nil:
519 "Cannot add Series: The Series already exists"
520 }
521
522 // Create the new Series
523 var newSeries <- create Series(
524 seriesId: seriesId,
525 metadata: metadata
526 )
527
528 // Add the new Series resource to the Series dictionary in the contract
529 AtlantaNft_NFT.series[seriesId] <-! newSeries
530 }
531
532 access(all) fun borrowSeries(seriesId: UInt32): &Series {
533 pre {
534 AtlantaNft_NFT.series[seriesId] != nil:
535 "Cannot borrow Series: The Series does not exist"
536 }
537
538 // Get a reference to the Series and return it
539 return (&AtlantaNft_NFT.series[seriesId])!
540 }
541
542 access(all) fun createNewAdmin(): @Admin {
543 return <-create Admin()
544 }
545
546 }
547
548 // This is the interface that users can cast their NFT Collection as
549 // to allow others to deposit AtlantaNft_NFT into their Collection. It also allows for reading
550 // the details of AtlantaNft_NFT in the Collection.
551 access(all) resource interface AtlantaNft_NFTCollectionPublic : NonFungibleToken.CollectionPublic {
552 access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection})
553 access(all) fun borrowAtlantaNft_NFT(id: UInt64): &NFT? {
554 // If the result isn't nil, the id of the returned reference
555 // should be the same as the argument to the function
556 post {
557 (result == nil) || (result?.id == id):
558 "Cannot borrow AtlantaNft_NFT reference: The ID of the returned reference is incorrect"
559 }
560 }
561 }
562
563 // Collection
564 // A collection of AtlantaNft_NFT NFTs owned by an account
565 //
566 access(all) resource Collection: AtlantaNft_NFTCollectionPublic, NonFungibleToken.Collection {
567 // dictionary of NFT conforming tokens
568 // NFT is a resource type with an UInt64 ID field
569 //
570 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
571
572 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
573 let supportedType: {Type: Bool} = {}
574 supportedType[Type<@AtlantaNft_NFT.NFT>()] = true
575 return supportedType
576 }
577
578 access(all) view fun isSupportedNFTType(type: Type): Bool {
579 if type == Type<@AtlantaNft_NFT.NFT>() {
580 return true
581 }
582 return false
583 }
584
585 // withdraw
586 // Removes an NFT from the collection and moves it to the caller
587 //
588 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
589 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
590
591 return <-token
592 }
593
594 // batchWithdraw withdraws multiple NFTs and returns them as a Collection
595 //
596 // Parameters: ids: An array of IDs to withdraw
597 //
598 // Returns: @NonFungibleToken.Collection: The collection of withdrawn tokens
599 //
600
601 access(NonFungibleToken.Withdraw) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
602 // Create a new empty Collection
603 var batchCollection <- create Collection()
604
605 // Iterate through the ids and withdraw them from the Collection
606 for id in ids {
607 batchCollection.deposit(token: <-self.withdraw(withdrawID: id))
608 }
609
610 // Return the withdrawn tokens
611 return <-batchCollection
612 }
613
614 // deposit
615 // Takes a NFT and adds it to the collections dictionary
616 // and adds the ID to the id array
617 //
618 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
619 let token <- token as! @AtlantaNft_NFT.NFT
620
621 let id: UInt64 = token.id
622
623 // add the new token to the dictionary which removes the old one
624 let oldToken <- self.ownedNFTs[id] <- token
625
626 destroy oldToken
627 }
628
629 // batchDeposit takes a Collection object as an argument
630 // and deposits each contained NFT into this Collection
631 access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection}) {
632
633 // Get an array of the IDs to be deposited
634 let keys = tokens.getIDs()
635
636 // Iterate through the keys in the collection and deposit each one
637 for key in keys {
638 self.deposit(token: <-tokens.withdraw(withdrawID: key))
639 }
640
641 // Destroy the empty Collection
642 destroy tokens
643 }
644
645 // getIDs
646 // Returns an array of the IDs that are in the collection
647 //
648 access(all) view fun getIDs(): [UInt64] {
649 return self.ownedNFTs.keys
650 }
651
652 access(all) view fun getLength(): Int {
653 return self.ownedNFTs.keys.length
654 }
655
656 // borrowNFT
657 // Gets a reference to an NFT in the collection
658 // so that the caller can read its metadata and call its methods
659 //
660 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
661 return &self.ownedNFTs[id]
662 }
663
664 // borrowAtlantaNft_NFT
665 // Gets a reference to an NFT in the collection as a AtlantaNft_NFT,
666 // exposing all of its fields.
667 // This is safe as there are no functions that can be called on the AtlantaNft_NFT.
668 //
669 access(all) fun borrowAtlantaNft_NFT(id: UInt64): &NFT? {
670 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
671 return nft as! &NFT
672 }
673 return nil
674 }
675
676 // borrowViewResolver
677 // Gets a reference to the MetadataViews resolver in the collection,
678 // giving access to all metadata information made available.
679 //
680 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
681 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
682 return nft as &{ViewResolver.Resolver}
683 }
684 return nil
685 }
686
687 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
688 return <- create Collection()
689 }
690
691 // initializer
692 //
693 init () {
694 self.ownedNFTs <- {}
695 }
696 }
697
698 // createEmptyCollection
699 // public function that anyone can call to create a new empty collection
700 //
701 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
702 return <- create Collection()
703 }
704
705 access(all) view fun getContractViews(resourceType: Type?): [Type] {
706 return [
707 Type<MetadataViews.NFTCollectionData>(),
708 Type<MetadataViews.NFTCollectionDisplay>()
709 ]
710 }
711
712 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
713 switch viewType {
714 case Type<MetadataViews.NFTCollectionData>():
715 return MetadataViews.NFTCollectionData(
716 storagePath: AtlantaNft_NFT.CollectionStoragePath,
717 publicPath: AtlantaNft_NFT.CollectionPublicPath,
718 publicCollection: Type<&AtlantaNft_NFT.Collection>(),
719 publicLinkedType: Type<&AtlantaNft_NFT.Collection>(),
720 createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
721 return <-AtlantaNft_NFT.createEmptyCollection(nftType: Type<@AtlantaNft_NFT.NFT>())
722 })
723 )
724 case Type<MetadataViews.NFTCollectionDisplay>():
725 let squareImage = MetadataViews.Media(
726 file: MetadataViews.HTTPFile(
727 url: "https://media-local.gigantik.io/atlantanft/square.png"
728 ),
729 mediaType: "image/png"
730 )
731 let bannerImage = MetadataViews.Media(
732 file: MetadataViews.HTTPFile(
733 url: "https://media-local.gigantik.io/atlantanft/banner.png"
734 ),
735 mediaType: "image/png"
736 )
737 var socials: {String: MetadataViews.ExternalURL} = {
738 "twitter": MetadataViews.ExternalURL("https://x.com/gigantik_"),
739 "discord": MetadataViews.ExternalURL("https://discord.gg/giglabs"),
740 "instagram": MetadataViews.ExternalURL("")
741 }
742
743 return MetadataViews.NFTCollectionDisplay(
744 name: "AtlantaNft shop",
745 description: "This is a AtlantaNft shop",
746 externalURL: MetadataViews.ExternalURL("https://AtlantaNft.shops.gigantik.io/"),
747 squareImage: squareImage,
748 bannerImage: bannerImage,
749 socials: socials
750 )
751 }
752 return nil
753 }
754
755 // getAllSeries returns all the sets
756 //
757 // Returns: An array of all the series that have been created
758 access(all) fun getAllSeries(): [AtlantaNft_NFT.SeriesData] {
759 return AtlantaNft_NFT.seriesData.values
760 }
761
762 // getAllSets returns all the sets
763 //
764 // Returns: An array of all the sets that have been created
765 access(all) fun getAllSets(): [AtlantaNft_NFT.NFTSetData] {
766 return AtlantaNft_NFT.setData.values
767 }
768
769 // getSeriesMetadata returns the metadata that the specified Series
770 // is associated with.
771 //
772 // Parameters: seriesId: The id of the Series that is being searched
773 //
774 // Returns: The metadata as a String to String mapping optional
775 access(all) fun getSeriesMetadata(seriesId: UInt32): {String: String}? {
776 return AtlantaNft_NFT.seriesData[seriesId]?.getMetadata()
777 }
778
779 // getSetMaxEditions returns the the maximum number of NFT editions that can
780 // be minted in this Set.
781 //
782 // Parameters: setId: The id of the Set that is being searched
783 //
784 // Returns: The max number of NFT editions in this Set
785 access(all) view fun getSetMaxEditions(setId: UInt32): UInt32? {
786 return AtlantaNft_NFT.setData[setId]?.maxEditions
787 }
788
789 // getSetMetadata returns all the metadata associated with a specific Set
790 //
791 // Parameters: setId: The id of the Set that is being searched
792 //
793 // Returns: The metadata as a String to String mapping optional
794 access(all) fun getSetMetadata(setId: UInt32): {String: String}? {
795 return AtlantaNft_NFT.setData[setId]?.getMetadata()
796 }
797
798 // getSetSeriesId returns the Series Id the Set belongs to
799 //
800 // Parameters: setId: The id of the Set that is being searched
801 //
802 // Returns: The Series Id
803 access(all) fun getSetSeriesId(setId: UInt32): UInt32? {
804 return AtlantaNft_NFT.setData[setId]?.seriesId
805 }
806
807 // getSetMetadata returns all the ipfs hashes for each nft
808 // edition in the Set.
809 //
810 // Parameters: setId: The id of the Set that is being searched
811 //
812 // Returns: The ipfs hashes of nft editions as a Array of Strings
813 access(all) fun getIpfsMetadataHashByNftEdition(setId: UInt32, editionNum: UInt32): String? {
814 // Don't force a revert if the setId or field is invalid
815 if let set = AtlantaNft_NFT.setData[setId] {
816 return set.getIpfsMetadataHash(editionNum: editionNum)
817 } else {
818 return nil
819 }
820 }
821
822 // getSetMetadataByField returns the metadata associated with a
823 // specific field of the metadata
824 //
825 // Parameters: setId: The id of the Set that is being searched
826 // field: The field to search for
827 //
828 // Returns: The metadata field as a String Optional
829 access(all) fun getSetMetadataByField(setId: UInt32, field: String): String? {
830 // Don't force a revert if the setId or field is invalid
831 if let set = AtlantaNft_NFT.setData[setId] {
832 return set.getMetadataField(field: field)
833 } else {
834 return nil
835 }
836 }
837
838 // stringToAddress Converts a string to a Flow address
839 //
840 // Parameters: input: The address as a String
841 //
842 // Returns: The flow address as an Address Optional
843 access(all) fun convertStringToAddress(_ input: String): Address? {
844 var address=input
845 if input.utf8[1] == 120 {
846 address = input.slice(from: 2, upTo: input.length)
847 }
848 var r:UInt64 = 0
849 var bytes = address.decodeHex()
850
851 while bytes.length>0{
852 r = r + (UInt64(bytes.removeFirst()) << UInt64(bytes.length * 8 ))
853 }
854
855 return Address(r)
856 }
857
858 // royaltyCutStringToUFix64 Converts a royalty cut string
859 // to a UFix64
860 //
861 // Parameters: royaltyCut: The cut value 0.0 - 1.0 as a String
862 //
863 // Returns: The royalty cut as a UFix64
864 access(all) fun royaltyCutStringToUFix64(_ royaltyCut: String): UFix64 {
865 var decimalPos = 0
866 if royaltyCut[0] == "." {
867 decimalPos = 1
868 } else if royaltyCut[1] == "." {
869 if royaltyCut[0] == "1" {
870 // "1" in the first postiion must be 1.0 i.e. 100% cut
871 return 1.0
872 } else if royaltyCut[0] == "0" {
873 decimalPos = 2
874 }
875 } else {
876 // Invalid royalty value
877 return 0.0
878 }
879
880 var royaltyCutStrLen = royaltyCut.length
881 if royaltyCut.length > (8 + decimalPos) {
882 // UFix64 is capped at 8 digits after the decimal
883 // so truncate excess decimal values from the string
884 royaltyCutStrLen = (8 + decimalPos)
885 }
886 let royaltyCutPercentValue = royaltyCut.slice(from: decimalPos, upTo: royaltyCutStrLen)
887 var bytes = royaltyCutPercentValue.utf8
888 var i = 0
889 var cutValueInteger: UInt64 = 0
890 var cutValueDivisor: UFix64 = 1.0
891 let zeroAsciiIntValue: UInt64 = 48
892 // First convert the string to a non-decimal Integer
893 while i < bytes.length {
894 cutValueInteger = (cutValueInteger * 10) + UInt64(bytes[i]) - zeroAsciiIntValue
895 cutValueDivisor = cutValueDivisor * 10.0
896 i = i + 1
897 }
898
899 // Convert the resulting Integer to a decimal in the range 0.0 - 0.99999999
900 return (UFix64(cutValueInteger) / cutValueDivisor)
901 }
902
903 // initializer
904 //
905 init() {
906 // Set named paths
907 self.CollectionStoragePath = /storage/AtlantaNft_NFTCollection
908 self.CollectionPublicPath = /public/AtlantaNft_NFTCollection
909 self.AdminStoragePath = /storage/AtlantaNft_NFTAdmin
910
911 self.setData = {}
912 self.seriesData = {}
913 self.series <- {}
914
915 // Put Admin in storage
916 self.account.storage.save(<-create Admin(), to: self.AdminStoragePath)
917
918 let collectionCap: Capability<&AtlantaNft_NFT.Collection> = self.account.capabilities.storage.issue<&AtlantaNft_NFT.Collection>(self.CollectionStoragePath)
919 self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
920 }
921}
922