Skip to content

Commit

Permalink
re-add basicNFT and UniversalCollection
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuahannan committed Mar 27, 2024
1 parent c01c72c commit 70c41d0
Show file tree
Hide file tree
Showing 2 changed files with 248 additions and 0 deletions.
137 changes: 137 additions & 0 deletions contracts/BasicNFT.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
*
* This is an basic implementation of a Flow Non-Fungible Token using the V2 standard.
* It shows that a basic NFT can be defined in very few lines of code (less than 100 here)
*
* Unlike the `ExampleNFT-v2` contract, this NFT illustrates a minimal implementation
* of an NFT that is now possible with the NFT standard since Events, collections,
* and other old requirements are not required any more.
*
* It also includes minimal metadata to showcase the simplicity
*
*/

import "NonFungibleToken"
import "MetadataViews"
import "ViewResolver"
import "UniversalCollection"

access(all) contract BasicNFT: NonFungibleToken {

/// The only thing that an NFT really needs to have is this resource definition
access(all) resource NFT: NonFungibleToken.NFT {
/// Arbitrary trait mapping metadata
access(self) let metadata: {String: AnyStruct}

access(all) let id: UInt64

init(
metadata: {String: AnyStruct},
) {
self.id = self.uuid
self.metadata = metadata
}

access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
return <- BasicNFT.createEmptyCollection(nftType: self.getType())
}

/// Uses the basic NFT views
access(all) view fun getViews(): [Type] {
return [
Type<MetadataViews.Display>(),
Type<MetadataViews.Serial>(),
Type<MetadataViews.Traits>(),
Type<MetadataViews.NFTCollectionData>(),
Type<MetadataViews.NFTCollectionDisplay>()
]
}

access(all) fun resolveView(_ view: Type): AnyStruct? {
switch view {
case Type<MetadataViews.Display>():
return MetadataViews.Display(
name: self.metadata["name"] as! String,
description: self.metadata["description"] as! String,
thumbnail: MetadataViews.HTTPFile(
url: self.metadata["thumbnail"] as! String
)
)
case Type<MetadataViews.Serial>():
return MetadataViews.Serial(
self.id
)
case Type<MetadataViews.Traits>():
return MetadataViews.dictToTraits(dict: self.metadata, excludedNames: nil)
case Type<MetadataViews.NFTCollectionData>():
return BasicNFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>())
case Type<MetadataViews.NFTCollectionDisplay>():
return BasicNFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionDisplay>())
}
return nil
}
}

access(all) view fun getContractViews(resourceType: Type?): [Type] {
return [
Type<MetadataViews.NFTCollectionData>(),
Type<MetadataViews.NFTCollectionDisplay>()
]
}

access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
switch viewType {
case Type<MetadataViews.NFTCollectionData>():
let collectionRef = self.account.storage.borrow<&UniversalCollection.Collection>(
from: /storage/flowBasicNFTCollection
) ?? panic("Could not borrow a reference to the stored collection")
let collectionData = MetadataViews.NFTCollectionData(
storagePath: collectionRef.storagePath,
publicPath: collectionRef.publicPath,
publicCollection: Type<&UniversalCollection.Collection>(),
publicLinkedType: Type<&UniversalCollection.Collection>(),
createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
return <-BasicNFT.createEmptyCollection(nftType: Type<@BasicNFT.NFT>())
})
)
return collectionData
case Type<MetadataViews.NFTCollectionDisplay>():
let media = MetadataViews.Media(
file: MetadataViews.HTTPFile(
url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
),
mediaType: "image/svg+xml"
)
return MetadataViews.NFTCollectionDisplay(
name: "The Example Collection",
description: "This collection is used as an example to help you develop your next Flow NFT.",
externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
squareImage: media,
bannerImage: media,
socials: {
"twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
}
)
}
return nil
}

access(all) resource NFTMinter {
access(all) fun mintNFT(metadata: {String: AnyStruct}): @BasicNFT.NFT {
return <- create NFT(metadata: metadata)
}
}

access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
return <- UniversalCollection.createEmptyCollection(identifier: "flowBasicNFTCollection", type: Type<@BasicNFT.NFT>())
}

init() {
let minter <- create NFTMinter()
self.account.storage.save(<-minter, to: /storage/flowBasicNFTMinterPath)

let collection <- self.createEmptyCollection(nftType: Type<@BasicNFT.NFT>())
self.account.storage.save(<-collection, to: /storage/flowBasicNFTCollection)
}
}

111 changes: 111 additions & 0 deletions contracts/UniversalCollection.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
*
* This is an example collection that can store any one type of NFT
* The Collection is restricted to one NFT type.
* This allows developers to write NFT contracts without having
* to also write all of the Collection boilerplate code,
* saving many lines of code.
*
*/

import "NonFungibleToken"
import "MetadataViews"
import "ViewResolver"

access(all) contract UniversalCollection {

/// The typical Collection resource, but one that anyone can use
///
access(all) resource Collection: NonFungibleToken.Collection {

/// every Universal collection supports a single type
/// All deposits and withdrawals must be of this type
access(all) let supportedType : Type

/// The path identifier
access(all) let identifier: String

/// Dictionary mapping NFT IDs to the stored NFTs
access(contract) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}

access(all) var storagePath: StoragePath
access(all) var publicPath: PublicPath

access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
return <- create Collection(identifier: self.identifier, type: self.supportedType)
}

init (identifier: String, type:Type) {
self.ownedNFTs <- {}
self.identifier = identifier
self.supportedType = type
self.storagePath = StoragePath(identifier: identifier)!
self.publicPath = PublicPath(identifier: identifier)!
}

/// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
let supportedTypes: {Type: Bool} = {}
supportedTypes[self.supportedType] = true
return supportedTypes
}

/// Returns whether or not the given type is accepted by the collection
access(all) view fun isSupportedNFTType(type: Type): Bool {
if type == self.supportedType {
return true
} else {
return false
}
}

/// withdraw removes an NFT from the collection and moves it to the caller
access(NonFungibleToken.Withdraw | NonFungibleToken.Owner) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
let token <- self.ownedNFTs.remove(key: withdrawID)
?? panic("Could not withdraw an NFT with the ID: ".concat(withdrawID.toString()).concat(" from the collection"))

return <-token
}

/// deposit takes a NFT and adds it to the collections dictionary
/// and adds the ID to the id array
access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
if self.supportedType != token.getType() {
panic("Cannot deposit an NFT of the given type")
}

// add the new token to the dictionary which removes the old one
let oldToken <- self.ownedNFTs[token.id] <- token
destroy oldToken
}

/// getIDs returns an array of the IDs that are in the collection
access(all) view fun getIDs(): [UInt64] {
return self.ownedNFTs.keys
}

/// getLength retusnt the number of items in the collection
access(all) view fun getLength(): Int {
return self.ownedNFTs.length
}

/// Borrows a reference to an NFT in the collection if it is there
/// otherwise, returns `nil`
access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
}

/// Borrow the view resolver for the specified NFT ID
access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
return (&self.ownedNFTs[id] as &{ViewResolver.Resolver}?)!
}
}

/// Public function that anyone can call to create
/// a new empty collection with the specified type restriction
/// NFT contracts can include a call to this method in
/// their own createEmptyCollection method
access(all) fun createEmptyCollection(identifier: String, type: Type): @{NonFungibleToken.Collection} {
return <- create Collection(identifier: identifier, type:type)
}
}

0 comments on commit 70c41d0

Please sign in to comment.