onflow / flow-nft

The non-fungible token standard on the Flow blockchain
https://onflow.org
The Unlicense
465 stars 169 forks source link
blockchain linear-types nft onflow smart-contracts

Flow Non-Fungible Token Standard

This standard defines the minimum functionality required to implement a safe, secure, and easy-to-use non-fungible token contract on the Flow blockchain

The version of the contracts in the master branch is the Cadence 1.0 version of the contracts and is not the same as the ones that are currently deployed to testnet and mainnet. See the cadence-0.42 branch for the currently deployed versions.

What is Cadence?

Cadence is the resource-oriented programming language for developing smart contracts on Flow.

Before reading this standard, we recommend completing the Cadence tutorials to build a basic understanding of the programming language.

Resource-oriented programming, and by extension Cadence, provides an ideal programming model for non-fungible tokens (NFTs). Users are able to store their NFT objects directly in their accounts and transact peer-to-peer. Learn more in this blog post about resources.

Import Addresses

The NonFungibleToken, ViewResolver, and MetadataViews contracts are already deployed on various networks. You can import them in your contracts from these addresses. There is no need to deploy them yourself.

Note: With the emulator, you must use the -contracts flag to deploy these contracts.

Network Contract Address
Emulator/Canary 0xf8d6e0586b0a20c7
PreviewNet 0xb6763b4399a888c8
Testnet/Crescendo 0x631e88ae7f1d7c20
Mainnet 0x1d7e57aa55817448

Core Types

Contracts that implement the NonFungibleToken interface are expected to utilize two resource interfaces:

Core Features

The NonFungibleToken contract defines the following set of functionality that should be included in each implementation:

Create a new NFT collection

Create a new collection using the Token.createEmptyCollection(nftType: Type) function.

This function MUST return an empty collection that contains no NFTs.

Users typically save new collections to a contract-defined location in their account and public a capability to their collection.

Withdraw an NFT

Withdraw an NFT from a Collection using the withdraw() function. This function emits the NonFungibleToken.Withdrawn event automatically.

Deposit an NFT

Deposit an NFT into a Collection using the deposit() function. This function emits the NonFungibleToken.Deposited event automatically.

⚠️ Important

In order to comply with the deposit function in the interface, an implementation MUST take a @{NonFungibleToken.NFT} resource as an argument. This means that anyone can send a resource object that conforms to {NonFungibleToken.NFT} to a deposit function. In an implementation, you MUST cast the token as your specific token type before depositing it or you will deposit another token type into your collection. For example:

/// `ExampleNFT` much be changed to the name of your contract
let token <- token as! @ExampleNFT.NFT

List NFTs in an account

Return a list of NFTs in a Collection using the getIDs function.

Return the NFT type that a collection can accept in a deposit

Return types of NFTs that a Collection can accept in a deposit using the getSupportedNFTTypes functions.

Get Available SubNFTs, if any

Some NFTs can own other NFTs, the standard provides a function that projects can optionally implement to return information the owned NFTs.

NFT Metadata

The primary documentation for metadata views is on the Flow developer portal. Please refer to that for the most thorough exploration of the views with examples.

NFT metadata is represented in a flexible and modular way using the standard proposed in FLIP-0636.

When writing an NFT contract, you should implement the MetadataViews.Resolver interface, which allows your NFT to utilize one or more metadata types called views.

Each view represents a different type of metadata, such as an on-chain creator biography or an off-chain video clip. Views do not specify or require how to store your metadata, they only specify the format to query and return them, so projects can still be flexible with how they store their data.

How to read metadata

This example shows how to read basic information about an NFT including the name, description, image and owner.

Source: get_nft_metadata.cdc

How to implement metadata

The example NFT contract shows a basic example for how to implement metadata views.

List of views

The views marked as Core views are considered the minimum required views to provide a full picture of any NFT. If you want your NFT to be able to be easily accessed and understood by third-party apps such as the Flow NFT Catalog it should implement all of them as a pre-requisite.

Name Purpose Status Source Core view
NFTView Basic view that includes the name, description and thumbnail. Implemented MetadataViews.cdc
Display Return the basic representation of an NFT. Implemented MetadataViews.cdc :white_check_mark:
HTTPFile A file available at an HTTP(S) URL. Implemented MetadataViews.cdc
IPFSFile A file stored in IPFS. Implemented MetadataViews.cdc
Edition Return information about one or more editions for an NFT. Implemented MetadataViews.cdc
Editions Wrapper for multiple edition views. Implemented MetadataViews.cdc
Serial Serial number for an NFT. Implemented MetadataViews.cdc :white_check_mark:
Royalty A Royalty Cut for a given NFT. Implemented MetadataViews.cdc
Royalties Wrapper for multiple Royalty views. Implemented MetadataViews.cdc :white_check_mark:
Media Represents a file with a corresponding mediaType Implemented MetadataViews.cdc
Medias Wrapper for multiple Media views. Implemented MetadataViews.cdc
License Represents a license according to https://spdx.org/licenses/ Implemented MetadataViews.cdc
ExternalURL Exposes a URL to an NFT on an external site. Implemented MetadataViews.cdc :white_check_mark:
NFTCollectionData Provides storage and retrieval information of an NFT Implemented MetadataViews.cdc :white_check_mark:
NFTCollectionDisplay Returns the basic representation of an NFT's Collection. Implemented MetadataViews.cdc :white_check_mark:
Rarity Expose rarity information for an NFT Implemented MetadataViews.cdc
Trait Represents a single field of metadata on an NFT. Implemented MetadataViews.cdc
Traits Wrapper for multiple Trait views Implemented MetadataViews.cdc :white_check_mark:

How to propose a new view

Please open a issue or a pull request to propose a new metadata view or changes to an existing view.

Feedback

We'd love to hear from anyone who has feedback. For example:

Please create an issue in this repository if there is a feature that you believe needs discussing or changing.

Comparison to other standards on Ethereum

This standard covers much of the same ground as ERC-721 and ERC-1155, but without most of the downsides.

How to test the standard

If you want to test out these contracts, we recommend either testing them with the Flow Playground or with the Visual Studio Code Extension.

If you are not making/testing any modifications to the standard contracts, they are already deployed to the addresses listed above and you can just import from those directly instead of deploying them yourself.

If you want to test changes to the standards, the steps to follow are:

  1. Deploy ViewResolver.cdc
  2. Deploy NonFungibleToken.cdc, importing ViewResolver.
  3. Deploy ExampleNFT.cdc, importing NonFungibleToken.

Then you can experiment with some of the other transactions and scripts in transactions/ or even write your own. You'll need to replace some of the import address placeholders with addresses that you deploy to, as well as some of the transaction arguments.

Running automated tests

You can find automated tests written in the Cadence testing framework in the tests/ directory. Use flow test tests/test_example_nft.cdc to run these tests.

More tests, written in Go, are in the lib/go/test/ directory. They use the transaction templates package that is contained in the lib/go/templates/ directory. To run the Go tests, you can run make test from the repository root. Contract and transaction assets must be generated before individual tests can be run, so if you are wanting to run the tests individually via go test, you must run make generate from within the lib/go/ directory after every revision you make to the contract or transaction files.

License

The works in these files:

are under the Unlicense.