-
Notifications
You must be signed in to change notification settings - Fork 170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UniversalCollection
and BasicNFT
Contracts
#208
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should not be ! at the end.